LCOV - code coverage report
Current view: top level - src - parser.cpp (source / functions) Coverage Total Hit
Test: JSON Expression Parser Lines: 95.4 % 327 312
Test Date: 2024-11-04 20:34:32 Functions: 92.0 % 25 23
Branches: 69.2 % 620 429

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * The MIT License (MIT)
       3                 :             :  *
       4                 :             :  * Copyright (c) 2024 Yaroslav Riabtsev <yaroslav.riabtsev@rwth-aachen.de>
       5                 :             :  *
       6                 :             :  * Permission is hereby granted, free of charge, to any person obtaining a copy
       7                 :             :  * of this software and associated documentation files (the "Software"), to deal
       8                 :             :  * in the Software without restriction, including without limitation the rights
       9                 :             :  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      10                 :             :  * copies of the Software, and to permit persons to whom the Software is
      11                 :             :  * furnished to do so, subject to the following conditions:
      12                 :             :  *
      13                 :             :  * The above copyright notice and this permission notice shall be included
      14                 :             :  * in all copies or substantial portions of the Software.
      15                 :             :  *
      16                 :             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      17                 :             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      18                 :             :  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
      19                 :             :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      20                 :             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      21                 :             :  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      22                 :             :  * SOFTWARE.
      23                 :             :  */
      24                 :             : 
      25                 :             : #include "parser.hpp"
      26                 :             : 
      27                 :             : #include <cassert>
      28                 :             : #include <ranges>
      29                 :             : #include <utility>
      30                 :             : 
      31                 :          34 : std::runtime_error parser_lib::parser::throw_message(
      32                 :             :     const std::string& message, const std::source_location location
      33                 :             : ) const {
      34         [ +  - ]:          34 :     std::ostringstream oss;
      35   [ +  -  +  -  :          34 :     oss << "[Parser-Error] " << message << ". ";
                   +  - ]
      36                 :             : #ifndef NDEBUG
      37         [ +  - ]:          34 :     if (!ifs.is_open()) {
      38         [ +  - ]:          34 :         oss << "No input stream open. ";
      39                 :             :     }
      40         [ +  + ]:          34 :     if (!valid()) {
      41         [ +  - ]:          14 :         oss << "Position is out of range. Line: " << (line + 1)
      42   [ +  -  +  -  :          14 :             << ", position: " << (pos + 1) << " exceeds available input. ";
             +  -  +  - ]
      43                 :             :     } else {
      44                 :          20 :         const char current_char = peek();
      45                 :             :         oss << "Character '" << current_char
      46   [ +  -  +  -  :          20 :             << "' (ASCII: " << static_cast<int>(current_char)
                   +  - ]
      47   [ +  -  +  -  :          20 :             << ") was found at line " << (line + 1) << ", position "
             +  -  +  - ]
      48   [ +  -  +  - ]:          20 :             << (pos + 1) << ". ";
      49                 :             :     }
      50   [ +  -  +  -  :          34 :     oss << "In file: " << location.file_name() << '(' << location.line() << ':'
          +  -  +  -  +  
                      - ]
      51   [ +  -  +  -  :          34 :         << location.column() << ") `" << location.function_name() << "`";
             +  -  +  - ]
      52                 :             : #endif
      53   [ +  -  +  - ]:          68 :     return std::runtime_error(oss.str());
      54                 :          34 : }
      55                 :             : 
      56                 :         106 : parser_lib::parser::parser(std::string& buffer)
      57                 :         106 :     : buffer(std::move(buffer))
      58                 :         106 :     , pos(0)
      59                 :         106 :     , line(0) { }
      60                 :             : 
      61                 :           0 : parser_lib::parser::parser(const std::stringstream& ss)
      62         [ #  # ]:           0 :     : buffer(ss.str())
      63                 :           0 :     , pos(0)
      64                 :           0 :     , line(0) { }
      65                 :             : 
      66                 :           0 : parser_lib::parser::parser(std::ifstream& is)
      67                 :           0 :     : ifs(move(is)) {
      68         [ #  # ]:           0 :     read_line();
      69                 :           0 : }
      70                 :             : 
      71                 :           5 : parser_lib::parser::parser(const std::filesystem::path& path) {
      72         [ +  - ]:           5 :     ifs.open(path);
      73         [ +  + ]:           5 :     if (!ifs.is_open()) {
      74                 :             :         throw std::invalid_argument(
      75         [ +  - ]:           2 :             "failed to open file with path: " + path.string()
      76   [ +  -  +  - ]:           3 :         );
      77                 :             :     }
      78         [ +  - ]:           4 :     read_line();
      79                 :           6 : }
      80                 :             : 
      81                 :         110 : void parser_lib::parser::completely_parse_json(
      82                 :             :     std::shared_ptr<json_lib::json>& result, const bool dynamic
      83                 :             : ) {
      84                 :         110 :     parse_json(result, dynamic);
      85                 :          74 :     nonessential();
      86         [ +  + ]:          74 :     if (result == nullptr) {
      87   [ +  -  +  - ]:           9 :         throw throw_message("json is empty");
      88                 :             :     }
      89         [ +  + ]:          71 :     if (valid()) {
      90   [ +  -  +  - ]:           6 :         throw throw_message("invalid json");
      91                 :             :     }
      92                 :          69 : }
      93                 :             : 
      94                 :     2002973 : size_t parser_lib::parser::get_pos() const {
      95         [ -  + ]:     2002973 :     assert(pos >= 0 && "stream is not empty");
      96                 :     2002973 :     return static_cast<size_t>(pos);
      97                 :             : }
      98                 :             : 
      99                 :      703629 : bool parser_lib::parser::valid() const {
     100   [ +  +  +  - ]:      703629 :     return pos >= 0 && get_pos() < buffer.size();
     101                 :             : }
     102                 :             : 
     103                 :      510238 : void parser_lib::parser::next() {
     104         [ -  + ]:      510238 :     assert(pos >= 0 && "stream is not empty");
     105                 :      510238 :     ++pos;
     106         [ +  + ]:      510238 :     if (get_pos() >= buffer.size()) {
     107                 :        5077 :         read_line();
     108                 :             :     }
     109                 :      510238 : }
     110                 :             : 
     111                 :        5081 : void parser_lib::parser::read_line() {
     112                 :        5081 :     ++line;
     113                 :        5081 :     buffer.clear();
     114                 :        5081 :     pos = -1;
     115         [ +  + ]:        5081 :     if (ifs.eof()) {
     116                 :           4 :         ifs.close();
     117                 :             :     }
     118         [ +  + ]:        5081 :     if (!ifs.is_open()) {
     119                 :          90 :         return;
     120                 :             :     }
     121                 :        4991 :     std::getline(ifs, buffer);
     122                 :        4991 :     buffer += '\n';
     123                 :        4991 :     pos = 0;
     124                 :             : }
     125                 :             : 
     126                 :      789530 : char parser_lib::parser::peek() const { return buffer[get_pos()]; }
     127                 :             : 
     128                 :        8751 : char parser_lib::parser::get() {
     129                 :        8751 :     const char peek_char = peek();
     130                 :        8751 :     next();
     131                 :        8751 :     return peek_char;
     132                 :             : }
     133                 :             : 
     134                 :          15 : bool parser_lib::parser::check_ahead(const char expected) const {
     135         [ -  + ]:          15 :     if (!valid()) {
     136                 :           0 :         return false;
     137                 :             :     }
     138                 :          15 :     const size_t next_index = get_pos() + 1;
     139         [ -  + ]:          15 :     if (next_index >= buffer.size()) {
     140                 :           0 :         return false;
     141                 :             :     }
     142                 :          15 :     return buffer[next_index] == expected;
     143                 :             : }
     144                 :             : 
     145                 :       25144 : bool parser_lib::parser::separator(const char val) {
     146                 :       25144 :     nonessential();
     147   [ +  +  +  +  :       25144 :     if (valid() && peek() == val) {
                   +  + ]
     148                 :       22705 :         next();
     149                 :       22705 :         nonessential();
     150                 :       22705 :         return true;
     151                 :             :     }
     152                 :        2439 :     return false;
     153                 :             : }
     154                 :             : 
     155                 :       67329 : void parser_lib::parser::nonessential() {
     156   [ +  +  +  +  :      115570 :     while (valid() && std::isspace(peek())) {
                   +  + ]
     157                 :       48241 :         next();
     158                 :             :     }
     159   [ +  +  +  +  :       67329 :     if (valid() && peek() == '/' && check_ahead('/')) {
             +  -  +  + ]
     160   [ +  -  +  +  :         387 :         while (valid() && get() != '\n')
                   +  + ]
     161                 :             :             ;
     162                 :          15 :         nonessential();
     163                 :             :     }
     164                 :       67329 : }
     165                 :             : 
     166                 :          96 : std::string parser_lib::parser::parse_keyword() {
     167   [ +  -  -  +  :          96 :     assert(
                   -  - ]
     168                 :             :         valid() && (std::isalpha(peek()) || peek() == '_')
     169                 :             :         && "expected keyword starting with a letter"
     170                 :             :     );
     171                 :          96 :     std::string keyword;
     172                 :             :     do {
     173   [ +  -  +  - ]:         452 :         keyword += get();
     174   [ +  +  +  +  :         452 :     } while (valid() && (std::isalnum(peek()) || peek() == '_'));
             -  +  +  + ]
     175                 :          96 :     return keyword;
     176                 :           0 : }
     177                 :             : 
     178                 :       19488 : std::string parser_lib::parser::parse_string() {
     179   [ +  -  +  - ]:       19488 :     assert(valid() && peek() == '\"' && "expected opening quote");
     180                 :       19488 :     next();
     181                 :       19488 :     std::string value;
     182                 :       19488 :     bool is_backslash = false;
     183                 :             :     do {
     184                 :      406066 :         const char current = peek();
     185         [ +  + ]:      406066 :         if (is_backslash) {
     186   [ +  +  +  +  :          21 :             switch (current) {
          +  +  +  +  +  
                      + ]
     187                 :           5 :             case '\"':
     188         [ +  - ]:           5 :                 value += '\"';
     189                 :           5 :                 break;
     190                 :           5 :             case '\\':
     191         [ +  - ]:           5 :                 value += '\\';
     192                 :           5 :                 break;
     193                 :           1 :             case '/':
     194         [ +  - ]:           1 :                 value += '/';
     195                 :           1 :                 break;
     196                 :           1 :             case 'b':
     197         [ +  - ]:           1 :                 value += '\b';
     198                 :           1 :                 break;
     199                 :           1 :             case 'f':
     200         [ +  - ]:           1 :                 value += '\f';
     201                 :           1 :                 break;
     202                 :           2 :             case 'n':
     203         [ +  - ]:           2 :                 value += '\n';
     204                 :           2 :                 break;
     205                 :           1 :             case 'r':
     206         [ +  - ]:           1 :                 value += '\r';
     207                 :           1 :                 break;
     208                 :           1 :             case 't':
     209         [ +  - ]:           1 :                 value += '\t';
     210                 :           1 :                 break;
     211                 :           2 :             case 'u': {
     212                 :           2 :                 std::string hex;
     213         [ +  + ]:           8 :                 for (int i = 0; i < 4; ++i) {
     214         [ +  - ]:           7 :                     next();
     215   [ +  -  +  +  :           7 :                     if (!valid() || !isxdigit(peek())) {
                   +  + ]
     216                 :             :                         throw throw_message(
     217                 :             :                             "invalid Unicode escape sequence in JSON string"
     218   [ +  -  +  - ]:           3 :                         );
     219                 :             :                     }
     220         [ +  - ]:           6 :                     hex += peek();
     221                 :             :                 }
     222         [ +  - ]:           1 :                 const int codepoint = std::stoi(hex, nullptr, 16);
     223                 :             :                 std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t>
     224         [ +  - ]:           1 :                     converter;
     225   [ +  -  +  - ]:           1 :                 value += converter.to_bytes(static_cast<char32_t>(codepoint));
     226                 :           1 :                 break;
     227                 :           2 :             }
     228                 :           2 :             default:
     229   [ +  -  +  - ]:           6 :                 throw throw_message("invalid escape sequence in JSON string");
     230                 :             :             }
     231                 :          18 :             is_backslash = false;
     232         [ +  + ]:      406045 :         } else if (current == '\\') {
     233                 :          21 :             is_backslash = true;
     234         [ +  + ]:      406024 :         } else if (current == '\"') {
     235                 :       19484 :             break;
     236                 :             :         } else {
     237         [ +  - ]:      386540 :             value += current;
     238                 :             :         }
     239         [ +  - ]:      386579 :         next();
     240         [ +  + ]:      386579 :     } while (valid());
     241   [ +  +  -  +  :       19485 :     if (!valid() || peek() != '\"') {
                   +  + ]
     242   [ +  -  +  - ]:           3 :         throw throw_message("invalid JSON string: Missing closing quote");
     243                 :             :     }
     244         [ +  - ]:       19484 :     next();
     245                 :       19484 :     return value;
     246                 :           4 : }
     247                 :             : 
     248                 :        3306 : std::tuple<std::string, bool> parser_lib::parser::parse_number() {
     249         [ +  - ]:        3306 :     nonessential();
     250                 :        3306 :     std::string number;
     251                 :        3306 :     bool is_float = false;
     252   [ +  -  +  +  :        3306 :     if (valid() && peek() == '-') {
                   +  + ]
     253   [ +  -  +  - ]:           4 :         number += get();
     254                 :             :     }
     255   [ +  +  +  +  :        3306 :     if (valid() && peek() == '0') {
                   +  + ]
     256   [ +  -  +  - ]:          16 :         number += get();
     257   [ +  +  +  +  :          16 :         if (valid() && std::isdigit(peek())) {
                   +  + ]
     258                 :             :             throw throw_message(
     259                 :             :                 "invalid number format: leading zeros are not allowed"
     260   [ +  -  +  - ]:           3 :             );
     261                 :             :         }
     262   [ +  +  +  -  :        3290 :     } else if (valid() && std::isdigit(peek())) {
                   +  + ]
     263                 :             :         do {
     264   [ +  -  +  - ]:        5708 :             number += get();
     265   [ +  +  +  +  :        5708 :         } while (valid() && std::isdigit(peek()));
                   +  + ]
     266                 :             :     } else {
     267   [ +  -  +  - ]:           3 :         throw throw_message("invalid number format: expected digit");
     268                 :             :     }
     269   [ +  +  +  +  :        3304 :     if (valid() && peek() == '.') {
                   +  + ]
     270                 :         795 :         is_float = true;
     271   [ +  -  +  - ]:         795 :         number += get();
     272   [ +  +  -  +  :         795 :         if (!valid() || !std::isdigit(peek())) {
                   +  + ]
     273                 :             :             throw throw_message(
     274                 :             :                 "invalid number format: expected digit after decimal"
     275   [ +  -  +  - ]:           3 :             );
     276                 :             :         }
     277   [ +  +  +  +  :        2170 :         while (valid() && std::isdigit(peek())) {
                   +  + ]
     278   [ +  -  +  - ]:        1376 :             number += get();
     279                 :             :         }
     280                 :             :     }
     281   [ +  +  +  +  :        3303 :     if (valid() && (peek() == 'e' || peek() == 'E')) {
             -  +  +  + ]
     282                 :           5 :         is_float = true;
     283   [ +  -  +  - ]:           5 :         number += get();
     284   [ +  +  +  +  :           5 :         if (valid() && (peek() == '+' || peek() == '-')) {
             +  +  +  + ]
     285   [ +  -  +  - ]:           3 :             number += get();
     286                 :             :         }
     287   [ +  +  -  +  :           5 :         if (!valid() || !std::isdigit(peek())) {
                   +  + ]
     288                 :             :             throw throw_message(
     289                 :             :                 "invalid number format: expected digit after exponent"
     290   [ +  -  +  - ]:           3 :             );
     291                 :             :         }
     292   [ +  +  +  -  :           9 :         while (valid() && std::isdigit(peek())) {
                   +  + ]
     293   [ +  -  +  - ]:           5 :             number += get();
     294                 :             :         }
     295                 :             :     }
     296         [ +  - ]:        3302 :     assert(!number.empty() && "number is not empty");
     297         [ +  - ]:        6604 :     return { number, is_float };
     298                 :        3306 : }
     299                 :             : 
     300                 :        1138 : void parser_lib::parser::parse_array_item(
     301                 :             :     std::vector<std::shared_ptr<json_lib::json>>& children, const bool dynamic
     302                 :             : ) {
     303                 :        1138 :     std::shared_ptr<json_lib::json> child;
     304         [ +  + ]:        1138 :     parse_json(child, dynamic);
     305         [ +  - ]:        1135 :     children.emplace_back(child);
     306                 :        1138 : }
     307                 :             : 
     308                 :          20 : void parser_lib::parser::parse_set_item(
     309                 :             :     std::vector<std::shared_ptr<reference_lib::json_reference>>& children, bool
     310                 :             : ) {
     311                 :             :     auto tail = std::make_shared<reference_lib::json_reference>(
     312                 :           0 :         reference_lib::ref_head_type::accessor
     313         [ +  - ]:          20 :     );
     314         [ +  - ]:          20 :     parse_tail(tail);
     315   [ +  -  +  + ]:          20 :     if (tail->length() == 0) {
     316   [ +  -  +  - ]:           6 :         throw throw_message("expected path");
     317                 :             :     }
     318         [ +  - ]:          18 :     children.emplace_back(tail);
     319                 :          20 : }
     320                 :             : 
     321                 :       12001 : void parser_lib::parser::parse_object_item(
     322                 :             :     std::vector<std::pair<std::string, std::shared_ptr<json_lib::json>>>&
     323                 :             :         children,
     324                 :             :     const bool dynamic
     325                 :             : ) {
     326   [ +  -  +  +  :       12001 :     if (!valid() || peek() != '\"') {
                   +  + ]
     327   [ +  -  +  - ]:           9 :         throw throw_message("expected key as a string");
     328                 :             :     }
     329         [ +  - ]:       11998 :     std::string key = parse_string();
     330   [ +  -  +  + ]:       11998 :     if (!separator(':')) {
     331   [ +  -  +  - ]:           3 :         throw throw_message("expected key-value separator for json object");
     332                 :             :     }
     333                 :       11997 :     std::shared_ptr<json_lib::json> value;
     334         [ +  + ]:       11997 :     parse_json(value, dynamic);
     335         [ +  - ]:       11993 :     children.emplace_back(key, value);
     336                 :       12002 : }
     337                 :             : 
     338                 :         374 : bool parser_lib::parser::parse_accessor(
     339                 :             :     std::shared_ptr<json_lib::json>& accessor
     340                 :             : ) {
     341                 :         374 :     nonessential();
     342         [ +  + ]:         374 :     if (!valid()) {
     343                 :          36 :         return false;
     344                 :             :     }
     345         [ +  + ]:         338 :     if (peek() == '.') {
     346         [ +  - ]:          60 :         next();
     347   [ +  +  +  -  :          60 :         if (!std::isalpha(peek()) && peek() != '_') {
                   +  + ]
     348   [ +  -  +  - ]:           3 :             throw throw_message("invalid const accessor");
     349                 :             :         }
     350         [ +  - ]:          59 :         const std::string keyword = parse_keyword();
     351   [ +  +  -  +  :          59 :         if (valid() && peek() == '(') {
                   -  + ]
     352   [ #  #  #  # ]:           0 :             throw throw_message("suffix-functions not yet supported");
     353                 :             :             next();
     354                 :             :             const auto function
     355                 :             :                 = std::make_shared<reference_lib::json_function>(keyword);
     356                 :             :             function->set_args(
     357                 :             :                 parse_collection<std::vector<std::shared_ptr<json_lib::json>>>(
     358                 :             :                     true, ')', &parser::parse_array_item
     359                 :             :                 )
     360                 :             :             );
     361                 :             :             accessor = function;
     362                 :             :         } else {
     363         [ +  - ]:          59 :             accessor = std::make_shared<json_lib::json_string>(keyword);
     364                 :             :         }
     365         [ +  + ]:         337 :     } else if (peek() == '[') {
     366         [ +  - ]:          55 :         next();
     367                 :             :         auto keys
     368                 :             :             = parse_collection<std::vector<std::shared_ptr<json_lib::json>>>(
     369                 :             :                 true, ']', &parser::parse_array_item
     370         [ +  + ]:          55 :             );
     371         [ +  + ]:          54 :         if (keys.size() == 1) {
     372                 :          51 :             accessor = keys[0];
     373                 :             :         } else {
     374                 :             :             std::vector<std::shared_ptr<reference_lib::json_reference>>
     375                 :           3 :                 ref_keys;
     376         [ +  - ]:           3 :             ref_keys.reserve(keys.size());
     377         [ +  + ]:          17 :             for (const auto& key : keys) {
     378                 :             :                 auto ref_accessor
     379                 :             :                     = std::make_shared<reference_lib::json_reference>(
     380                 :           0 :                         reference_lib::ref_head_type::accessor
     381         [ +  - ]:          14 :                     );
     382         [ +  - ]:          14 :                 ref_accessor->emplace_back(key);
     383         [ +  - ]:          14 :                 ref_keys.emplace_back(ref_accessor);
     384                 :          14 :             }
     385         [ +  - ]:           3 :             accessor = std::make_shared<reference_lib::json_set>(ref_keys);
     386                 :           3 :         }
     387         [ +  + ]:         277 :     } else if (peek() == '{') {
     388         [ +  - ]:           7 :         next();
     389                 :             :         auto accessors = parse_collection<
     390                 :             :             std::vector<std::shared_ptr<reference_lib::json_reference>>>(
     391                 :             :             true, '}', &parser::parse_set_item
     392         [ +  + ]:           7 :         );
     393         [ +  - ]:           5 :         accessor = std::make_shared<reference_lib::json_set>(accessors);
     394                 :           5 :     } else {
     395                 :         216 :         return false;
     396                 :             :     }
     397                 :         118 :     return true;
     398                 :             : }
     399                 :             : 
     400                 :         262 : void parser_lib::parser::parse_tail(
     401                 :             :     const std::shared_ptr<reference_lib::json_reference>& result
     402                 :             : ) {
     403   [ +  +  +  + ]:         374 :     for (std::shared_ptr<json_lib::json> accessor; parse_accessor(accessor);) {
     404         [ +  + ]:         118 :         result->emplace_back(accessor);
     405                 :             :         // todo: reference = reference->ref_value();
     406                 :         262 :     }
     407                 :         252 : }
     408                 :             : 
     409                 :         242 : void parser_lib::parser::parse_reference(std::shared_ptr<json_lib::json>& result
     410                 :             : ) {
     411                 :         242 :     std::shared_ptr<reference_lib::json_reference> reference;
     412   [ +  -  +  + ]:         242 :     if (result->type() == json_lib::json_type::reference_json) {
     413                 :             :         reference
     414                 :          60 :             = std::dynamic_pointer_cast<reference_lib::json_reference>(result);
     415                 :             :     } else {
     416         [ +  - ]:         182 :         reference = std::make_shared<reference_lib::json_reference>(result);
     417                 :             :     }
     418         [ +  + ]:         242 :     parse_tail(reference);
     419         [ +  - ]:         232 :     result = reference->value();
     420                 :         242 : }
     421                 :             : 
     422                 :       13252 : void parser_lib::parser::parse_json(
     423                 :             :     std::shared_ptr<json_lib::json>& result, const bool dynamic
     424                 :             : ) {
     425                 :       13252 :     nonessential();
     426         [ +  + ]:       13252 :     if (!valid()) {
     427                 :           3 :         return;
     428                 :             :     }
     429   [ +  +  +  +  :       13249 :     if (dynamic && peek() == '@') {
                   +  + ]
     430                 :          10 :         result = std::make_shared<reference_lib::json_reference>(
     431         [ +  - ]:          10 :             reference_lib::ref_head_type::local
     432                 :          10 :         );
     433                 :          10 :         next();
     434   [ +  +  +  +  :       13239 :     } else if (dynamic && peek() == '$') {
                   +  + ]
     435                 :          17 :         result = std::make_shared<reference_lib::json_reference>(
     436         [ +  - ]:          17 :             reference_lib::ref_head_type::root
     437                 :          17 :         );
     438                 :          17 :         next();
     439   [ +  +  -  +  :       13222 :     } else if (std::isalpha(peek()) || peek() == '_') {
                   +  + ]
     440   [ +  -  +  -  :          37 :         if (const std::string keyword = parse_keyword(); keyword == "true") {
                   +  + ]
     441         [ +  - ]:           4 :             result = std::make_shared<json_lib::json_boolean>(true);
     442   [ +  -  +  + ]:          33 :         } else if (keyword == "false") {
     443         [ +  - ]:           2 :             result = std::make_shared<json_lib::json_boolean>(false);
     444   [ +  -  +  + ]:          31 :         } else if (keyword == "null") {
     445         [ +  - ]:           3 :             result = std::make_shared<json_lib::json>();
     446         [ +  + ]:          28 :         } else if (dynamic) {
     447   [ +  -  +  +  :          27 :             if (valid() && peek() == '(') {
                   +  + ]
     448         [ +  - ]:          12 :                 next();
     449                 :             :                 const auto ref
     450         [ +  - ]:          12 :                     = std::make_shared<reference_lib::json_function>(keyword);
     451         [ +  - ]:          24 :                 ref->set_args(parse_collection<
     452         [ +  - ]:          24 :                               std::vector<std::shared_ptr<json_lib::json>>>(
     453                 :             :                     dynamic, ')', &parser::parse_array_item
     454                 :             :                 ));
     455                 :          12 :                 result = ref;
     456                 :          12 :             } else {
     457                 :             :                 const auto ref
     458                 :             :                     = std::make_shared<reference_lib::json_reference>(
     459                 :           0 :                         reference_lib::ref_head_type::root
     460         [ +  - ]:          15 :                     );
     461         [ +  - ]:          30 :                 ref->emplace_back(
     462         [ +  - ]:          30 :                     std::make_shared<json_lib::json_string>(keyword)
     463                 :             :                 );
     464                 :          15 :                 result = ref;
     465                 :          15 :             }
     466                 :             :         } else {
     467   [ +  -  +  - ]:           3 :             throw throw_message("invalid json");
     468                 :          37 :         }
     469   [ +  +  +  +  :       13185 :     } else if (peek() == '-' || std::isdigit(peek())) {
                   +  + ]
     470   [ +  +  +  + ]:        3306 :         if (auto [number, is_float] = parse_number(); is_float) {
     471                 :             :             // const float value = std::stof(number);
     472         [ +  - ]:         795 :             result = std::make_shared<json_lib::json_real>(number);
     473                 :             :         } else {
     474         [ +  - ]:        2507 :             const int value = std::stoi(number);
     475         [ +  - ]:        2507 :             result = std::make_shared<json_lib::json_integer>(value);
     476                 :        3302 :         }
     477         [ +  + ]:        9879 :     } else if (peek() == '\"') {
     478   [ +  +  +  - ]:        7490 :         result = std::make_shared<json_lib::json_string>(parse_string());
     479         [ +  + ]:        2389 :     } else if (peek() == '[') {
     480         [ +  - ]:          97 :         next();
     481                 :             :         auto children
     482                 :             :             = parse_collection<std::vector<std::shared_ptr<json_lib::json>>>(
     483                 :             :                 dynamic, ']', &parser::parse_array_item
     484         [ +  + ]:          97 :             );
     485         [ +  - ]:          90 :         result = std::make_shared<json_lib::json_array>(children);
     486         [ +  + ]:          90 :         if (dynamic) {
     487         [ +  - ]:          24 :             result->touch();
     488                 :             :         }
     489         [ +  + ]:        2382 :     } else if (peek() == '{') {
     490         [ +  - ]:        2281 :         next();
     491                 :             :         auto children = parse_collection<std::vector<
     492                 :             :             std::pair<std::string, std::shared_ptr<json_lib::json>>>>(
     493                 :             :             dynamic, '}', &parser::parse_object_item
     494         [ +  + ]:        2281 :         );
     495         [ +  + ]:        2270 :         result = std::make_shared<json_lib::json_object>(children);
     496         [ +  + ]:        2269 :         if (dynamic) {
     497         [ +  - ]:          12 :             result->touch();
     498                 :             :         }
     499   [ +  +  +  -  :        2281 :     } else if (dynamic && peek() == '(') {
                   +  + ]
     500                 :           7 :         next();
     501                 :           7 :         parse_json(result, dynamic);
     502                 :           7 :         nonessential();
     503   [ +  -  +  +  :           7 :         if (!valid() || peek() != ')') {
                   +  + ]
     504   [ +  -  +  - ]:           3 :             throw throw_message("invalid json");
     505                 :             :         }
     506                 :           6 :         next();
     507                 :             :     } else {
     508   [ +  -  +  - ]:          12 :         throw throw_message("invalid json");
     509                 :             :     }
     510         [ +  + ]:       13216 :     if (dynamic) {
     511                 :         242 :         parse_reference(result);
     512                 :             :         // todo: math (arithmetic binary operators: +, -, *, /...)
     513                 :             :     }
     514                 :             : }
        

Generated by: LCOV version 2.0-1