LCOV - code coverage report
Current view: top level - EnergyPlus/InputProcessing - IdfParser.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 85.3 % 510 435
Test Date: 2025-06-02 12:03:30 Functions: 88.9 % 27 24

            Line data    Source code
       1              : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
       2              : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3              : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4              : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5              : // contributors. All rights reserved.
       6              : //
       7              : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8              : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9              : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10              : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11              : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12              : //
      13              : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14              : // provided that the following conditions are met:
      15              : //
      16              : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17              : //     conditions and the following disclaimer.
      18              : //
      19              : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20              : //     conditions and the following disclaimer in the documentation and/or other materials
      21              : //     provided with the distribution.
      22              : //
      23              : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24              : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25              : //     used to endorse or promote products derived from this software without specific prior
      26              : //     written permission.
      27              : //
      28              : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29              : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30              : //     reference solely to the software portion of its product, Licensee must refer to the
      31              : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32              : //     obtained under this License and may not use a different name for the software. Except as
      33              : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34              : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35              : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36              : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37              : //
      38              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39              : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40              : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41              : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42              : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43              : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45              : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46              : // POSSIBILITY OF SUCH DAMAGE.
      47              : 
      48              : #include <EnergyPlus/FromChars.hh>
      49              : #include <EnergyPlus/InputProcessing/IdfParser.hh>
      50              : #include <cmath>
      51              : #include <fast_float/fast_float.h>
      52              : #include <fmt/format.h>
      53              : #include <milo/dtoa.h>
      54              : #include <milo/itoa.h>
      55              : 
      56              : using json = nlohmann::json;
      57              : 
      58       985574 : auto const icompare = [](std::string_view a, std::string_view b) { // (AUTO_OK)
      59       985574 :     return (a.length() == b.length()
      60      1495560 :                 ? std::equal(a.begin(), a.end(), b.begin(), [](char const c, char const d) { return (::tolower(c) == ::tolower(d)); })
      61       985574 :                 : 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         1365 : json IdfParser::decode(std::string_view idf, json const &schema, bool &success)
      71              : {
      72         1365 :     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         1365 : json IdfParser::decode(std::string_view idf, size_t _idf_size, json const &schema, bool &success)
      82              : {
      83         1365 :     success = true;
      84         1365 :     cur_line_num = 1;
      85         1365 :     index_into_cur_line = 0;
      86         1365 :     beginning_of_line_index = 0;
      87         1365 :     idf_size = _idf_size;
      88              : 
      89         1365 :     if (idf.empty()) {
      90            0 :         success = false;
      91            0 :         return nullptr;
      92              :     }
      93              : 
      94         1365 :     size_t index = 0;
      95         1365 :     return parse_idf(idf, index, success, schema);
      96              : }
      97              : 
      98            5 : std::string IdfParser::encode(json const &root, json const &schema)
      99              : {
     100              :     static constexpr std::string_view end_of_field(",\n  ", 4);
     101              :     static constexpr std::string_view end_of_object(";\n\n", 3);
     102              : 
     103            5 :     std::string encoded, extension_key;
     104            5 :     if (idf_size > 0) {
     105            5 :         encoded.reserve(idf_size);
     106              :     } else {
     107            0 :         encoded.reserve(root.size() * 1024);
     108              :     }
     109              : 
     110           29 :     for (auto obj = root.begin(); obj != root.end(); ++obj) {
     111           24 :         const auto &legacy_idd = schema["properties"][obj.key()]["legacy_idd"];
     112           24 :         const auto &legacy_idd_field = legacy_idd["fields"];
     113           24 :         auto key = legacy_idd.find("extension");
     114           24 :         if (key != legacy_idd.end()) {
     115            2 :             extension_key = key.value().get<std::string>();
     116              :         }
     117           48 :         for (auto obj_in = obj.value().begin(); obj_in != obj.value().end(); ++obj_in) {
     118           24 :             encoded += obj.key();
     119           24 :             size_t skipped_fields = 0;
     120          134 :             for (size_t i = 0; i < legacy_idd_field.size(); i++) {
     121          110 :                 std::string const &entry = legacy_idd_field[i].get<std::string>();
     122          110 :                 if (obj_in.value().find(entry) == obj_in.value().end()) {
     123           16 :                     if (entry == "name") {
     124           27 :                         encoded += std::string{end_of_field} + obj_in.key();
     125              :                     } else {
     126            7 :                         skipped_fields++;
     127              :                     }
     128           16 :                     continue;
     129              :                 }
     130          100 :                 for (size_t j = 0; j < skipped_fields; j++) {
     131            6 :                     encoded += end_of_field;
     132              :                 }
     133           94 :                 skipped_fields = 0;
     134           94 :                 encoded += end_of_field;
     135           94 :                 auto const &val = obj_in.value()[entry];
     136           94 :                 if (val.is_string()) {
     137           52 :                     encoded += val.get<std::string>();
     138           42 :                 } else if (val.is_number_integer()) {
     139           21 :                     encoded += std::to_string(val.get<int>());
     140              :                 } else {
     141           21 :                     dtoa(val.get<double>(), s);
     142           21 :                     encoded += s;
     143              :                 }
     144          110 :             }
     145              : 
     146           24 :             if (obj_in.value().find(extension_key) == obj_in.value().end()) {
     147           22 :                 encoded += end_of_object;
     148           22 :                 continue;
     149              :             }
     150              : 
     151            2 :             auto &extensions = obj_in.value()[extension_key];
     152           24 :             for (size_t extension_i = 0; extension_i < extensions.size(); extension_i++) {
     153           22 :                 auto const &cur_extension_obj = extensions[extension_i];
     154           22 :                 auto const &extensible = schema["properties"][obj.key()]["legacy_idd"]["extensibles"];
     155           52 :                 for (size_t i = 0; i < extensible.size(); i++) {
     156           30 :                     std::string const &tmp = extensible[i].get<std::string>();
     157           30 :                     if (cur_extension_obj.find(tmp) == cur_extension_obj.end()) {
     158            1 :                         skipped_fields++;
     159            1 :                         continue;
     160              :                     }
     161           30 :                     for (size_t j = 0; j < skipped_fields; j++) {
     162            1 :                         encoded += end_of_field;
     163              :                     }
     164           29 :                     skipped_fields = 0;
     165           29 :                     encoded += end_of_field;
     166           29 :                     if (cur_extension_obj[tmp].is_string()) {
     167           17 :                         encoded += cur_extension_obj[tmp].get<std::string>();
     168              :                     } else {
     169           12 :                         dtoa(cur_extension_obj[tmp].get<double>(), s);
     170           12 :                         encoded += s;
     171              :                     }
     172           30 :                 }
     173              :             }
     174            2 :             encoded += end_of_object;
     175           24 :         }
     176           29 :     }
     177           10 :     return encoded;
     178            5 : }
     179              : 
     180        30831 : std::string IdfParser::normalizeObjectType(std::string const &objectType)
     181              : {
     182        30831 :     if (objectType.empty()) {
     183            0 :         return std::string{};
     184              :     }
     185        30831 :     std::string key = convertToUpper(objectType);
     186        30831 :     auto tmp_umit = objectTypeMap.find(key);
     187        30831 :     if (tmp_umit != objectTypeMap.end()) {
     188        30830 :         return tmp_umit->second;
     189              :     }
     190            1 :     return std::string{};
     191        30831 : }
     192              : 
     193         1365 : std::vector<std::string> const &IdfParser::errors()
     194              : {
     195         1365 :     return errors_;
     196              : }
     197              : 
     198         1365 : std::vector<std::string> const &IdfParser::warnings()
     199              : {
     200         1365 :     return warnings_;
     201              : }
     202              : 
     203         1341 : bool IdfParser::hasErrors()
     204              : {
     205         1341 :     return !errors_.empty();
     206              : }
     207              : 
     208         1365 : json IdfParser::parse_idf(std::string_view idf, size_t &index, bool &success, json const &schema)
     209              : {
     210         1365 :     json root;
     211              :     Token token;
     212         1365 :     auto const &schema_properties = schema["properties"];
     213              : 
     214         1365 :     objectTypeMap.reserve(schema_properties.size());
     215      1160250 :     for (auto it = schema_properties.begin(); it != schema_properties.end(); ++it) {
     216      1158885 :         std::string key = convertToUpper(it.key());
     217      1158885 :         objectTypeMap.emplace(std::move(key), it.key());
     218      1160250 :     }
     219              : 
     220         1365 :     if (idf_size > 3) {
     221              :         // UTF-8 Byte Order Mark
     222         1365 :         if (idf[0] == '\xEF' && idf[1] == '\xBB' && idf[2] == '\xBF') {
     223            1 :             index += 3;
     224            1 :             index_into_cur_line += 3;
     225              :         }
     226              :     }
     227              : 
     228         1365 :     int idfObjectCount = 0;
     229              :     while (true) {
     230        80251 :         token = look_ahead(idf, index);
     231        80251 :         if (token == Token::END) {
     232         1365 :             break;
     233        78886 :         } else if (token == Token::NONE) {
     234            0 :             success = false;
     235            0 :             return root;
     236        78886 :         } else if (token == Token::SEMICOLON) {
     237            0 :             next_token(idf, index);
     238            0 :             continue;
     239        78886 :         } 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        78886 :         } else if (token == Token::EXCLAMATION) {
     244        48055 :             eat_comment(idf, index);
     245              :         } else {
     246        30831 :             ++idfObjectCount;
     247        30831 :             std::string const parsed_obj_name = parse_string(idf, index);
     248        30831 :             std::string const obj_name = normalizeObjectType(parsed_obj_name);
     249        30831 :             if (obj_name.empty()) {
     250            2 :                 errors_.emplace_back(
     251            2 :                     fmt::format("Line: {} Index: {} - \"{}\" is not a valid Object Type.", cur_line_num, index_into_cur_line, parsed_obj_name));
     252            4 :                 while (token != Token::SEMICOLON && token != Token::END) {
     253            3 :                     token = next_token(idf, index);
     254              :                 }
     255            1 :                 continue;
     256              :             }
     257              : 
     258        30830 :             bool object_success = true;
     259        30830 :             json const &obj_loc = schema_properties[obj_name];
     260        30830 :             json const &legacy_idd = obj_loc["legacy_idd"];
     261        30830 :             json obj = parse_object(idf, index, object_success, legacy_idd, obj_loc, idfObjectCount);
     262        30830 :             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        30830 :             u64toa(root[obj_name].size() + 1, s);
     275        30830 :             std::string name = fmt::format("{} {}", obj_name, s);
     276              : 
     277        30830 :             if (!obj.is_null()) {
     278        30830 :                 auto const name_iter = obj.find("name");
     279              :                 // If you find a name field, use that
     280        30830 :                 if (name_iter != obj.end()) {
     281        24102 :                     name = name_iter.value().get<std::string>();
     282        24102 :                     obj.erase(name_iter);
     283              :                 } else {
     284              :                     // Otherwise, see if it should have a name field
     285         6728 :                     auto const it = obj_loc.find("name");
     286         6728 :                     if (it != obj_loc.end()) {
     287              :                         // Let it slide, as a blank string, to be handled in the appropriate GetInput routine
     288            4 :                         name = "";
     289              :                     }
     290         6728 :                 }
     291        30830 :             }
     292              : 
     293        30830 :             if (root[obj_name].find(name) != root[obj_name].end()) {
     294            1 :                 errors_.emplace_back(
     295            1 :                     fmt::format(R"(Duplicate name found for object of type "{}" named "{}". Overwriting existing object.)", obj_name, name));
     296              :             }
     297              : 
     298        30830 :             root[obj_name][name] = std::move(obj);
     299        30832 :         }
     300        78886 :     }
     301              : 
     302         1365 :     return root;
     303            0 : }
     304              : 
     305        30830 : 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        30830 :     json root = json::object();
     309        30830 :     json extensible = json::object();
     310        30830 :     json array_of_extensions = json::array();
     311              :     Token token;
     312        30830 :     std::string extension_key;
     313        30830 :     size_t legacy_idd_index = 0;
     314        30830 :     size_t extensible_index = 0;
     315        30830 :     success = true;
     316        30830 :     bool was_value_parsed = false;
     317        30830 :     auto const &legacy_idd_fields_array = legacy_idd["fields"];
     318        30830 :     auto const legacy_idd_extensibles_iter = legacy_idd.find("extensibles");
     319              : 
     320        30830 :     auto const &schema_patternProperties = schema_obj_loc["patternProperties"];
     321        30830 :     std::string patternProperty;
     322        61660 :     int dot_star_present = schema_patternProperties.count(".*");
     323        30830 :     int no_whitespace_present = schema_patternProperties.count(R"(^.*\S.*$)");
     324        30830 :     if (dot_star_present) {
     325         7192 :         patternProperty = ".*";
     326        23638 :     } else if (no_whitespace_present) {
     327        23638 :         patternProperty = R"(^.*\S.*$)";
     328              :     } else {
     329            0 :         throw std::runtime_error(R"(The patternProperties value is not a valid choice (".*", "^.*\S.*$"))");
     330              :     }
     331        30830 :     auto const &schema_obj_props = schema_patternProperties[patternProperty]["properties"];
     332        30830 :     auto key = legacy_idd.find("extension");
     333              : 
     334        30830 :     json const *schema_obj_extensions = nullptr;
     335        30830 :     if (legacy_idd_extensibles_iter != legacy_idd.end()) {
     336         8217 :         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         8217 :         extension_key = key.value().get<std::string>();
     342         8217 :         schema_obj_extensions = &schema_obj_props[extension_key]["items"]["properties"];
     343              :     }
     344              : 
     345        30830 :     root["idf_order"] = idfObjectCount;
     346              : 
     347        30830 :     auto const found_min_fields = schema_obj_loc.find("min_fields");
     348              : 
     349        30830 :     index += 1;
     350              : 
     351              :     while (true) {
     352      2876055 :         token = look_ahead(idf, index);
     353      2876055 :         root["idf_max_fields"] = legacy_idd_index;
     354      2876055 :         root["idf_max_extensible_fields"] = extensible_index;
     355      2876055 :         if (token == Token::NONE) {
     356            0 :             success = false;
     357            0 :             return root;
     358      2876055 :         } else if (token == Token::END) {
     359            3 :             return root;
     360      2876052 :         } else if (token == Token::COMMA || token == Token::SEMICOLON) {
     361      1105754 :             if (!was_value_parsed) {
     362        31917 :                 int ext_size = 0;
     363        31917 :                 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         1314 :                     auto const &legacy_idd_extensibles_array = legacy_idd_extensibles_iter.value();
     368         1314 :                     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         1314 :                     extensible_index++;
     371              :                     //                    extensible[ field_name ] = "";
     372              :                 }
     373        31917 :                 if (ext_size && extensible_index % ext_size == 0) {
     374          325 :                     array_of_extensions.push_back(extensible);
     375          325 :                     extensible.clear();
     376              :                 }
     377              :             }
     378      1105754 :             legacy_idd_index++;
     379      1105754 :             was_value_parsed = false;
     380      1105754 :             next_token(idf, index);
     381      1105754 :             if (token == Token::SEMICOLON) {
     382        30827 :                 size_t min_fields = 0;
     383        30827 :                 if (found_min_fields != schema_obj_loc.end()) {
     384        13779 :                     min_fields = found_min_fields.value().get<size_t>();
     385              :                 }
     386        33893 :                 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        30827 :                 if (!extensible.empty()) {
     391          179 :                     array_of_extensions.push_back(extensible);
     392          179 :                     extensible.clear();
     393              :                 }
     394        30827 :                 root["idf_max_fields"] = legacy_idd_index;
     395        30827 :                 root["idf_max_extensible_fields"] = extensible_index;
     396        30827 :                 break;
     397              :             }
     398      2845225 :         } else if (token == Token::EXCLAMATION) {
     399       696460 :             eat_comment(idf, index);
     400      1073838 :         } else if (legacy_idd_index >= legacy_idd_fields_array.size()) {
     401       680584 :             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       680584 :             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       680584 :             auto const &legacy_idd_extensibles_array = legacy_idd_extensibles_iter.value();
     417       680584 :             size_t const size = legacy_idd_extensibles_array.size();
     418       680584 :             std::string const &field_name = legacy_idd_extensibles_array[extensible_index % size].get<std::string>();
     419       680584 :             json val = parse_value(idf, index, success, schema_obj_extensions->at(field_name));
     420       680584 :             if (!success) {
     421            0 :                 return root;
     422              :             }
     423       680584 :             extensible[field_name] = std::move(val);
     424       680584 :             was_value_parsed = true;
     425       680584 :             extensible_index++;
     426       680584 :             if (extensible_index && extensible_index % size == 0) {
     427       654885 :                 array_of_extensions.push_back(extensible);
     428       654885 :                 extensible.clear();
     429              :             }
     430       680584 :         } else {
     431       393254 :             was_value_parsed = true;
     432       393254 :             std::string const &field = legacy_idd_fields_array[legacy_idd_index].get<std::string>();
     433       393254 :             auto const find_field_iter = schema_obj_props.find(field);
     434       393254 :             if (find_field_iter == schema_obj_props.end()) {
     435        24102 :                 if (field == "name") {
     436        24102 :                     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       369152 :                 json val = parse_value(idf, index, success, find_field_iter.value());
     443       369152 :                 if (!success) {
     444            0 :                     return root;
     445              :                 }
     446       369152 :                 root[field] = std::move(val);
     447       369152 :             }
     448       393254 :             if (!success) {
     449            0 :                 return root;
     450              :             }
     451       393254 :         }
     452      2845225 :     }
     453        30827 :     if (!array_of_extensions.empty()) {
     454         7993 :         root[extension_key] = std::move(array_of_extensions);
     455         7993 :         array_of_extensions = nullptr;
     456              :     }
     457        30827 :     return root;
     458        30830 : }
     459              : 
     460       592367 : json IdfParser::parse_number(std::string_view idf, size_t &index)
     461              : {
     462       592367 :     eat_whitespace(idf, index);
     463              : 
     464       592367 :     size_t save_i = index;
     465              : 
     466       592367 :     bool running = true;
     467      5029424 :     while (running) {
     468      4437057 :         if (save_i == idf_size) {
     469            0 :             break;
     470              :         }
     471              : 
     472      4437057 :         char const c = idf[save_i];
     473      4437057 :         switch (c) {
     474       592367 :         case '!':
     475              :         case ',':
     476              :         case ';':
     477              :         case '\r':
     478              :         case '\n':
     479       592367 :             running = false;
     480       592367 :             break;
     481      3844690 :         default:
     482      3844690 :             ++save_i;
     483              :         }
     484              :     }
     485              : 
     486       592367 :     size_t diff = save_i - index;
     487       592367 :     std::string_view value = idf.substr(index, diff);
     488       592367 :     index_into_cur_line += diff;
     489       592367 :     index = save_i;
     490              : 
     491       435802 :     auto const convert_double = [&index, this](std::string_view str) -> json { // (AUTO_OK)
     492       435802 :         size_t plus_sign = 0;
     493       435802 :         if (str.front() == '+') {
     494            4 :             plus_sign = 1;
     495              :         }
     496       435802 :         auto const str_end = str.data() + str.size(); // have to do this for MSVC // (AUTO_OK)
     497              :         double val;
     498       435802 :         auto result = fast_float::from_chars(str.data() + plus_sign, str.data() + str.size(), val); // (AUTO_OK)
     499       435802 :         if (result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range) {
     500            3 :             return rtrim(str);
     501       435799 :         } else if (result.ptr != str_end) {
     502          230 :             auto const initial_ptr = result.ptr; // (AUTO_OK)
     503          456 :             while (result.ptr != str_end) {
     504          233 :                 if (*result.ptr != ' ') {
     505            7 :                     break;
     506              :                 }
     507          226 :                 ++result.ptr;
     508              :             }
     509          230 :             if (result.ptr == str_end) {
     510          223 :                 index -= (str_end - initial_ptr);
     511          223 :                 this->index_into_cur_line -= (str_end - initial_ptr);
     512          223 :                 return val;
     513              :             }
     514            7 :             return rtrim(str);
     515              :         }
     516       435569 :         return val;
     517       592367 :     };
     518              : 
     519       592367 :     auto const convert_int = [&convert_double, &index, this](std::string_view str) -> json { // (AUTO_OK)
     520       592367 :         auto const str_end = str.data() + str.size();                                        // have to do this for MSVC // (AUTO_OK)
     521              :         int val;
     522       592367 :         auto result = FromChars::from_chars(str.data(), str.data() + str.size(), val); // (AUTO_OK)
     523       592367 :         if (result.ec == std::errc::result_out_of_range || result.ec == std::errc::invalid_argument) {
     524         1438 :             return convert_double(str);
     525       590929 :         } else if (result.ptr != str_end) {
     526       434377 :             if (*result.ptr == '.' || *result.ptr == 'e' || *result.ptr == 'E') {
     527       434364 :                 return convert_double(str);
     528              :             } else {
     529           13 :                 auto const initial_ptr = result.ptr; // (AUTO_OK)
     530           22 :                 while (result.ptr != str_end) {
     531           13 :                     if (*result.ptr != ' ') {
     532            4 :                         break;
     533              :                     }
     534            9 :                     ++result.ptr;
     535              :                 }
     536           13 :                 if (result.ptr == str_end) {
     537            9 :                     index -= (str_end - initial_ptr);
     538            9 :                     this->index_into_cur_line -= (str_end - initial_ptr);
     539            9 :                     return val;
     540              :                 }
     541            4 :                 return rtrim(str);
     542              :             }
     543              :         }
     544       156552 :         return val;
     545       592367 :     };
     546              : 
     547      1184734 :     return convert_int(value);
     548              : }
     549              : 
     550         5498 : json IdfParser::parse_integer(std::string_view idf, size_t &index)
     551              : {
     552         5498 :     eat_whitespace(idf, index);
     553              : 
     554         5498 :     size_t save_i = index;
     555              : 
     556         5498 :     bool running = true;
     557        20190 :     while (running) {
     558        14692 :         if (save_i == idf_size) {
     559            0 :             break;
     560              :         }
     561              : 
     562        14692 :         char const c = idf[save_i];
     563        14692 :         switch (c) {
     564         5498 :         case '!':
     565              :         case ',':
     566              :         case ';':
     567              :         case '\r':
     568              :         case '\n':
     569         5498 :             running = false;
     570         5498 :             break;
     571         9194 :         default:
     572         9194 :             ++save_i;
     573              :         }
     574              :     }
     575              : 
     576         5498 :     size_t diff = save_i - index;
     577         5498 :     std::string_view string_value = idf.substr(index, diff);
     578         5498 :     index_into_cur_line += diff;
     579         5498 :     index = save_i;
     580              : 
     581         5498 :     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         5498 :     auto result = FromChars::from_chars(string_value.data(), string_value.data() + string_value.size(), int_value); // (AUTO_OK)
     585         5498 :     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         5498 :     } else if (result.ptr != string_end) {
     589              :         // Didn't use the entire string, try again via double conversion + rounding
     590           42 :         size_t plus_sign = 0;
     591           42 :         if (string_value.front() == '+') {
     592            0 :             plus_sign = 1;
     593              :         }
     594              :         double double_value;
     595           42 :         auto fresult = fast_float::from_chars(string_value.data() + plus_sign, string_value.data() + string_value.size(), double_value); // (AUTO_OK)
     596           42 :         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           42 :         int_value = static_cast<int>(std::round(double_value));
     601              :     }
     602         5498 :     return int_value;
     603              : }
     604              : 
     605      1049740 : json IdfParser::parse_value(std::string_view idf, size_t &index, bool &success, json const &field_loc)
     606              : {
     607              :     Token token;
     608      1049740 :     auto const &field_type = field_loc.find("type");
     609      1049740 :     if (field_type != field_loc.end()) {
     610       499879 :         if (field_type.value() == "number") {
     611       397859 :             token = Token::NUMBER;
     612       102020 :         } else if (field_type.value() == "integer") {
     613         5498 :             token = Token::INTEGER;
     614              :         } else {
     615        96522 :             token = Token::STRING;
     616              :         }
     617              :     } else {
     618       549861 :         token = look_ahead(idf, index);
     619              :     }
     620              : 
     621      1049740 :     switch (token) {
     622       451897 :     case Token::STRING: {
     623       451897 :         std::string const parsed_string = parse_string(idf, index);
     624       451897 :         auto const enum_it = field_loc.find("enum");
     625       451897 :         if (enum_it != field_loc.end()) {
     626       164180 :             for (auto const &enum_str : enum_it.value()) {
     627       164161 :                 std::string const str = enum_str.get<std::string>();
     628       164161 :                 if (icompare(str, parsed_string)) {
     629        39870 :                     return str;
     630              :                 }
     631       243920 :             }
     632       412008 :         } else if (icompare(parsed_string, "Autosize") || icompare(parsed_string, "Autocalculate")) {
     633         6806 :             auto const default_it = field_loc.find("default");
     634         3403 :             auto const anyOf_it = field_loc.find("anyOf");
     635              : 
     636         3403 :             if (anyOf_it == field_loc.end()) {
     637            4 :                 errors_.emplace_back(
     638            4 :                     fmt::format("Line: {} Index: {} - Field cannot be Autosize or Autocalculate", cur_line_num, index_into_cur_line));
     639            2 :                 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         3401 :             if (default_it != field_loc.end()) {
     644         3574 :                 return field_loc.at("anyOf")[1]["enum"][1];
     645              :             } else {
     646         3228 :                 return field_loc.at("anyOf")[1]["enum"][0];
     647              :             }
     648         3403 :         }
     649       408624 :         return parsed_string;
     650       451897 :     }
     651       592345 :     case Token::NUMBER: {
     652       592345 :         return parse_number(idf, index);
     653              :     }
     654         5498 :     case Token::INTEGER: {
     655         5498 :         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      1049740 : }
     668              : 
     669       506835 : std::string IdfParser::parse_string(std::string_view idf, size_t &index)
     670              : {
     671       506835 :     eat_whitespace(idf, index);
     672              : 
     673       506835 :     std::string str;
     674              : 
     675              :     while (true) {
     676      7494429 :         if (index == idf_size) {
     677            3 :             break;
     678              :         }
     679              : 
     680      7494426 :         char c = idf[index];
     681      7494426 :         increment_both_index(index, index_into_cur_line);
     682      7494426 :         if (c == ',' || c == ';' || c == '!') {
     683       506832 :             decrement_both_index(index, index_into_cur_line);
     684       506832 :             break;
     685              :         } else {
     686      6987594 :             str += c;
     687              :         }
     688      6987594 :     }
     689              : 
     690      1013670 :     return rtrim(str);
     691       506835 : }
     692              : 
     693     52075003 : void IdfParser::increment_both_index(size_t &index, size_t &line_index)
     694              : {
     695     52075003 :     index++;
     696     52075003 :     line_index++;
     697     52075003 : }
     698              : 
     699       506832 : void IdfParser::decrement_both_index(size_t &index, size_t &line_index)
     700              : {
     701       506832 :     index--;
     702       506832 :     line_index--;
     703       506832 : }
     704              : 
     705      5716640 : void IdfParser::eat_whitespace(std::string_view idf, size_t &index)
     706              : {
     707     22711924 :     while (index < idf_size) {
     708     22710554 :         switch (idf[index]) {
     709     16844987 :         case ' ':
     710              :         case '\r':
     711              :         case '\t':
     712     16844987 :             increment_both_index(index, index_into_cur_line);
     713     16844987 :             continue;
     714       150297 :         case '\n':
     715       150297 :             increment_both_index(index, cur_line_num);
     716       150297 :             beginning_of_line_index = index;
     717       150297 :             index_into_cur_line = 0;
     718       150297 :             continue;
     719      5715270 :         default:
     720      5715270 :             return;
     721              :         }
     722              :     }
     723              : }
     724              : 
     725       744519 : void IdfParser::eat_comment(std::string_view idf, size_t &index)
     726              : {
     727              :     while (true) {
     728     22974725 :         if (index == idf_size) {
     729            0 :             break;
     730              :         }
     731     22974725 :         if (idf[index] == '\n') {
     732       744519 :             increment_both_index(index, cur_line_num);
     733       744519 :             index_into_cur_line = 0;
     734       744519 :             beginning_of_line_index = index;
     735       744519 :             break;
     736              :         }
     737     22230206 :         increment_both_index(index, index_into_cur_line);
     738              :     }
     739       744519 : }
     740              : 
     741      3506174 : IdfParser::Token IdfParser::look_ahead(std::string_view idf, size_t index)
     742              : {
     743      3506174 :     size_t save_index = index;
     744      3506174 :     size_t save_line_num = cur_line_num;
     745      3506174 :     size_t save_line_index = index_into_cur_line;
     746      3506174 :     Token token = next_token(idf, save_index);
     747      3506174 :     cur_line_num = save_line_num;
     748      3506174 :     index_into_cur_line = save_line_index;
     749      3506174 :     return token;
     750              : }
     751              : 
     752      4611938 : IdfParser::Token IdfParser::next_token(std::string_view idf, size_t &index)
     753              : {
     754      4611938 :     eat_whitespace(idf, index);
     755              : 
     756      4611938 :     if (index == idf_size) {
     757         1370 :         return Token::END;
     758              :     }
     759              : 
     760      4610568 :     char const c = idf[index];
     761      4610568 :     increment_both_index(index, index_into_cur_line);
     762      4610568 :     switch (c) {
     763       744517 :     case '!':
     764       744517 :         return Token::EXCLAMATION;
     765      2149857 :     case ',':
     766      2149857 :         return Token::COMMA;
     767        61657 :     case ';':
     768        61657 :         return Token::SEMICOLON;
     769      1654537 :     default:
     770              :         static constexpr std::string_view numeric(".-+0123456789");
     771      1654537 :         if (numeric.find_first_of(c) != std::string::npos) {
     772       793332 :             return Token::NUMBER;
     773              :         }
     774       861205 :         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       506849 : std::string IdfParser::rtrim(std::string_view str)
     801              : {
     802              :     static constexpr std::string_view whitespace(" \t\0", 3);
     803       506849 :     if (str.empty()) {
     804            1 :         return std::string{};
     805              :     }
     806       506848 :     size_t const index = str.find_last_not_of(whitespace);
     807       506848 :     if (index == std::string::npos) {
     808            0 :         return std::string{};
     809       506848 :     } else if (index + 1 < str.length()) {
     810          897 :         return std::string{str.substr(0, index + 1)};
     811              :     }
     812      1013098 :     return std::string{str};
     813              : }
        

Generated by: LCOV version 2.0-1