1c5f01b2fSopenharmony_ci//     __ _____ _____ _____
2c5f01b2fSopenharmony_ci//  __|  |   __|     |   | |  JSON for Modern C++ (supporting code)
3c5f01b2fSopenharmony_ci// |  |  |__   |  |  | | | |  version 3.11.2
4c5f01b2fSopenharmony_ci// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
5c5f01b2fSopenharmony_ci//
6c5f01b2fSopenharmony_ci// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
7c5f01b2fSopenharmony_ci// SPDX-License-Identifier: MIT
8c5f01b2fSopenharmony_ci
9c5f01b2fSopenharmony_ci#include "doctest_compatibility.h"
10c5f01b2fSopenharmony_ci
11c5f01b2fSopenharmony_ci#define JSON_TESTS_PRIVATE
12c5f01b2fSopenharmony_ci#include <nlohmann/json.hpp>
13c5f01b2fSopenharmony_ciusing nlohmann::json;
14c5f01b2fSopenharmony_ci
15c5f01b2fSopenharmony_cinamespace
16c5f01b2fSopenharmony_ci{
17c5f01b2fSopenharmony_ci// shortcut to scan a string literal
18c5f01b2fSopenharmony_cijson::lexer::token_type scan_string(const char* s, bool ignore_comments = false);
19c5f01b2fSopenharmony_cijson::lexer::token_type scan_string(const char* s, const bool ignore_comments)
20c5f01b2fSopenharmony_ci{
21c5f01b2fSopenharmony_ci    auto ia = nlohmann::detail::input_adapter(s);
22c5f01b2fSopenharmony_ci    return nlohmann::detail::lexer<json, decltype(ia)>(std::move(ia), ignore_comments).scan(); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
23c5f01b2fSopenharmony_ci}
24c5f01b2fSopenharmony_ci} // namespace
25c5f01b2fSopenharmony_ci
26c5f01b2fSopenharmony_cistd::string get_error_message(const char* s, bool ignore_comments = false);
27c5f01b2fSopenharmony_cistd::string get_error_message(const char* s, const bool ignore_comments)
28c5f01b2fSopenharmony_ci{
29c5f01b2fSopenharmony_ci    auto ia = nlohmann::detail::input_adapter(s);
30c5f01b2fSopenharmony_ci    auto lexer = nlohmann::detail::lexer<json, decltype(ia)>(std::move(ia), ignore_comments); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
31c5f01b2fSopenharmony_ci    lexer.scan();
32c5f01b2fSopenharmony_ci    return lexer.get_error_message();
33c5f01b2fSopenharmony_ci}
34c5f01b2fSopenharmony_ci
35c5f01b2fSopenharmony_ciTEST_CASE("lexer class")
36c5f01b2fSopenharmony_ci{
37c5f01b2fSopenharmony_ci    SECTION("scan")
38c5f01b2fSopenharmony_ci    {
39c5f01b2fSopenharmony_ci        SECTION("structural characters")
40c5f01b2fSopenharmony_ci        {
41c5f01b2fSopenharmony_ci            CHECK((scan_string("[") == json::lexer::token_type::begin_array));
42c5f01b2fSopenharmony_ci            CHECK((scan_string("]") == json::lexer::token_type::end_array));
43c5f01b2fSopenharmony_ci            CHECK((scan_string("{") == json::lexer::token_type::begin_object));
44c5f01b2fSopenharmony_ci            CHECK((scan_string("}") == json::lexer::token_type::end_object));
45c5f01b2fSopenharmony_ci            CHECK((scan_string(",") == json::lexer::token_type::value_separator));
46c5f01b2fSopenharmony_ci            CHECK((scan_string(":") == json::lexer::token_type::name_separator));
47c5f01b2fSopenharmony_ci        }
48c5f01b2fSopenharmony_ci
49c5f01b2fSopenharmony_ci        SECTION("literal names")
50c5f01b2fSopenharmony_ci        {
51c5f01b2fSopenharmony_ci            CHECK((scan_string("null") == json::lexer::token_type::literal_null));
52c5f01b2fSopenharmony_ci            CHECK((scan_string("true") == json::lexer::token_type::literal_true));
53c5f01b2fSopenharmony_ci            CHECK((scan_string("false") == json::lexer::token_type::literal_false));
54c5f01b2fSopenharmony_ci        }
55c5f01b2fSopenharmony_ci
56c5f01b2fSopenharmony_ci        SECTION("numbers")
57c5f01b2fSopenharmony_ci        {
58c5f01b2fSopenharmony_ci            CHECK((scan_string("0") == json::lexer::token_type::value_unsigned));
59c5f01b2fSopenharmony_ci            CHECK((scan_string("1") == json::lexer::token_type::value_unsigned));
60c5f01b2fSopenharmony_ci            CHECK((scan_string("2") == json::lexer::token_type::value_unsigned));
61c5f01b2fSopenharmony_ci            CHECK((scan_string("3") == json::lexer::token_type::value_unsigned));
62c5f01b2fSopenharmony_ci            CHECK((scan_string("4") == json::lexer::token_type::value_unsigned));
63c5f01b2fSopenharmony_ci            CHECK((scan_string("5") == json::lexer::token_type::value_unsigned));
64c5f01b2fSopenharmony_ci            CHECK((scan_string("6") == json::lexer::token_type::value_unsigned));
65c5f01b2fSopenharmony_ci            CHECK((scan_string("7") == json::lexer::token_type::value_unsigned));
66c5f01b2fSopenharmony_ci            CHECK((scan_string("8") == json::lexer::token_type::value_unsigned));
67c5f01b2fSopenharmony_ci            CHECK((scan_string("9") == json::lexer::token_type::value_unsigned));
68c5f01b2fSopenharmony_ci
69c5f01b2fSopenharmony_ci            CHECK((scan_string("-0") == json::lexer::token_type::value_integer));
70c5f01b2fSopenharmony_ci            CHECK((scan_string("-1") == json::lexer::token_type::value_integer));
71c5f01b2fSopenharmony_ci
72c5f01b2fSopenharmony_ci            CHECK((scan_string("1.1") == json::lexer::token_type::value_float));
73c5f01b2fSopenharmony_ci            CHECK((scan_string("-1.1") == json::lexer::token_type::value_float));
74c5f01b2fSopenharmony_ci            CHECK((scan_string("1E10") == json::lexer::token_type::value_float));
75c5f01b2fSopenharmony_ci        }
76c5f01b2fSopenharmony_ci
77c5f01b2fSopenharmony_ci        SECTION("whitespace")
78c5f01b2fSopenharmony_ci        {
79c5f01b2fSopenharmony_ci            // result is end_of_input, because not token is following
80c5f01b2fSopenharmony_ci            CHECK((scan_string(" ") == json::lexer::token_type::end_of_input));
81c5f01b2fSopenharmony_ci            CHECK((scan_string("\t") == json::lexer::token_type::end_of_input));
82c5f01b2fSopenharmony_ci            CHECK((scan_string("\n") == json::lexer::token_type::end_of_input));
83c5f01b2fSopenharmony_ci            CHECK((scan_string("\r") == json::lexer::token_type::end_of_input));
84c5f01b2fSopenharmony_ci            CHECK((scan_string(" \t\n\r\n\t ") == json::lexer::token_type::end_of_input));
85c5f01b2fSopenharmony_ci        }
86c5f01b2fSopenharmony_ci    }
87c5f01b2fSopenharmony_ci
88c5f01b2fSopenharmony_ci    SECTION("token_type_name")
89c5f01b2fSopenharmony_ci    {
90c5f01b2fSopenharmony_ci        CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::uninitialized)) == "<uninitialized>"));
91c5f01b2fSopenharmony_ci        CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_true)) == "true literal"));
92c5f01b2fSopenharmony_ci        CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_false)) == "false literal"));
93c5f01b2fSopenharmony_ci        CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_null)) == "null literal"));
94c5f01b2fSopenharmony_ci        CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_string)) == "string literal"));
95c5f01b2fSopenharmony_ci        CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_unsigned)) == "number literal"));
96c5f01b2fSopenharmony_ci        CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_integer)) == "number literal"));
97c5f01b2fSopenharmony_ci        CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_float)) == "number literal"));
98c5f01b2fSopenharmony_ci        CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::begin_array)) == "'['"));
99c5f01b2fSopenharmony_ci        CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::begin_object)) == "'{'"));
100c5f01b2fSopenharmony_ci        CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_array)) == "']'"));
101c5f01b2fSopenharmony_ci        CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_object)) == "'}'"));
102c5f01b2fSopenharmony_ci        CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::name_separator)) == "':'"));
103c5f01b2fSopenharmony_ci        CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_separator)) == "','"));
104c5f01b2fSopenharmony_ci        CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::parse_error)) == "<parse error>"));
105c5f01b2fSopenharmony_ci        CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_of_input)) == "end of input"));
106c5f01b2fSopenharmony_ci    }
107c5f01b2fSopenharmony_ci
108c5f01b2fSopenharmony_ci    SECTION("parse errors on first character")
109c5f01b2fSopenharmony_ci    {
110c5f01b2fSopenharmony_ci        for (int c = 1; c < 128; ++c)
111c5f01b2fSopenharmony_ci        {
112c5f01b2fSopenharmony_ci            // create string from the ASCII code
113c5f01b2fSopenharmony_ci            const auto s = std::string(1, static_cast<char>(c));
114c5f01b2fSopenharmony_ci            // store scan() result
115c5f01b2fSopenharmony_ci            const auto res = scan_string(s.c_str());
116c5f01b2fSopenharmony_ci
117c5f01b2fSopenharmony_ci            CAPTURE(s)
118c5f01b2fSopenharmony_ci
119c5f01b2fSopenharmony_ci            switch (c)
120c5f01b2fSopenharmony_ci            {
121c5f01b2fSopenharmony_ci                // single characters that are valid tokens
122c5f01b2fSopenharmony_ci                case ('['):
123c5f01b2fSopenharmony_ci                case (']'):
124c5f01b2fSopenharmony_ci                case ('{'):
125c5f01b2fSopenharmony_ci                case ('}'):
126c5f01b2fSopenharmony_ci                case (','):
127c5f01b2fSopenharmony_ci                case (':'):
128c5f01b2fSopenharmony_ci                case ('0'):
129c5f01b2fSopenharmony_ci                case ('1'):
130c5f01b2fSopenharmony_ci                case ('2'):
131c5f01b2fSopenharmony_ci                case ('3'):
132c5f01b2fSopenharmony_ci                case ('4'):
133c5f01b2fSopenharmony_ci                case ('5'):
134c5f01b2fSopenharmony_ci                case ('6'):
135c5f01b2fSopenharmony_ci                case ('7'):
136c5f01b2fSopenharmony_ci                case ('8'):
137c5f01b2fSopenharmony_ci                case ('9'):
138c5f01b2fSopenharmony_ci                {
139c5f01b2fSopenharmony_ci                    CHECK((res != json::lexer::token_type::parse_error));
140c5f01b2fSopenharmony_ci                    break;
141c5f01b2fSopenharmony_ci                }
142c5f01b2fSopenharmony_ci
143c5f01b2fSopenharmony_ci                // whitespace
144c5f01b2fSopenharmony_ci                case (' '):
145c5f01b2fSopenharmony_ci                case ('\t'):
146c5f01b2fSopenharmony_ci                case ('\n'):
147c5f01b2fSopenharmony_ci                case ('\r'):
148c5f01b2fSopenharmony_ci                {
149c5f01b2fSopenharmony_ci                    CHECK((res == json::lexer::token_type::end_of_input));
150c5f01b2fSopenharmony_ci                    break;
151c5f01b2fSopenharmony_ci                }
152c5f01b2fSopenharmony_ci
153c5f01b2fSopenharmony_ci                // anything else is not expected
154c5f01b2fSopenharmony_ci                default:
155c5f01b2fSopenharmony_ci                {
156c5f01b2fSopenharmony_ci                    CHECK((res == json::lexer::token_type::parse_error));
157c5f01b2fSopenharmony_ci                    break;
158c5f01b2fSopenharmony_ci                }
159c5f01b2fSopenharmony_ci            }
160c5f01b2fSopenharmony_ci        }
161c5f01b2fSopenharmony_ci    }
162c5f01b2fSopenharmony_ci
163c5f01b2fSopenharmony_ci    SECTION("very large string")
164c5f01b2fSopenharmony_ci    {
165c5f01b2fSopenharmony_ci        // strings larger than 1024 bytes yield a resize of the lexer's yytext buffer
166c5f01b2fSopenharmony_ci        std::string s("\"");
167c5f01b2fSopenharmony_ci        s += std::string(2048, 'x');
168c5f01b2fSopenharmony_ci        s += "\"";
169c5f01b2fSopenharmony_ci        CHECK((scan_string(s.c_str()) == json::lexer::token_type::value_string));
170c5f01b2fSopenharmony_ci    }
171c5f01b2fSopenharmony_ci
172c5f01b2fSopenharmony_ci    SECTION("fail on comments")
173c5f01b2fSopenharmony_ci    {
174c5f01b2fSopenharmony_ci        CHECK((scan_string("/", false) == json::lexer::token_type::parse_error));
175c5f01b2fSopenharmony_ci        CHECK(get_error_message("/", false) == "invalid literal");
176c5f01b2fSopenharmony_ci
177c5f01b2fSopenharmony_ci        CHECK((scan_string("/!", false) == json::lexer::token_type::parse_error));
178c5f01b2fSopenharmony_ci        CHECK(get_error_message("/!", false) == "invalid literal");
179c5f01b2fSopenharmony_ci        CHECK((scan_string("/*", false) == json::lexer::token_type::parse_error));
180c5f01b2fSopenharmony_ci        CHECK(get_error_message("/*", false) == "invalid literal");
181c5f01b2fSopenharmony_ci        CHECK((scan_string("/**", false) == json::lexer::token_type::parse_error));
182c5f01b2fSopenharmony_ci        CHECK(get_error_message("/**", false) == "invalid literal");
183c5f01b2fSopenharmony_ci
184c5f01b2fSopenharmony_ci        CHECK((scan_string("//", false) == json::lexer::token_type::parse_error));
185c5f01b2fSopenharmony_ci        CHECK(get_error_message("//", false) == "invalid literal");
186c5f01b2fSopenharmony_ci        CHECK((scan_string("/**/", false) == json::lexer::token_type::parse_error));
187c5f01b2fSopenharmony_ci        CHECK(get_error_message("/**/", false) == "invalid literal");
188c5f01b2fSopenharmony_ci        CHECK((scan_string("/** /", false) == json::lexer::token_type::parse_error));
189c5f01b2fSopenharmony_ci        CHECK(get_error_message("/** /", false) == "invalid literal");
190c5f01b2fSopenharmony_ci
191c5f01b2fSopenharmony_ci        CHECK((scan_string("/***/", false) == json::lexer::token_type::parse_error));
192c5f01b2fSopenharmony_ci        CHECK(get_error_message("/***/", false) == "invalid literal");
193c5f01b2fSopenharmony_ci        CHECK((scan_string("/* true */", false) == json::lexer::token_type::parse_error));
194c5f01b2fSopenharmony_ci        CHECK(get_error_message("/* true */", false) == "invalid literal");
195c5f01b2fSopenharmony_ci        CHECK((scan_string("/*/**/", false) == json::lexer::token_type::parse_error));
196c5f01b2fSopenharmony_ci        CHECK(get_error_message("/*/**/", false) == "invalid literal");
197c5f01b2fSopenharmony_ci        CHECK((scan_string("/*/* */", false) == json::lexer::token_type::parse_error));
198c5f01b2fSopenharmony_ci        CHECK(get_error_message("/*/* */", false) == "invalid literal");
199c5f01b2fSopenharmony_ci    }
200c5f01b2fSopenharmony_ci
201c5f01b2fSopenharmony_ci    SECTION("ignore comments")
202c5f01b2fSopenharmony_ci    {
203c5f01b2fSopenharmony_ci        CHECK((scan_string("/", true) == json::lexer::token_type::parse_error));
204c5f01b2fSopenharmony_ci        CHECK(get_error_message("/", true) == "invalid comment; expecting '/' or '*' after '/'");
205c5f01b2fSopenharmony_ci
206c5f01b2fSopenharmony_ci        CHECK((scan_string("/!", true) == json::lexer::token_type::parse_error));
207c5f01b2fSopenharmony_ci        CHECK(get_error_message("/!", true) == "invalid comment; expecting '/' or '*' after '/'");
208c5f01b2fSopenharmony_ci        CHECK((scan_string("/*", true) == json::lexer::token_type::parse_error));
209c5f01b2fSopenharmony_ci        CHECK(get_error_message("/*", true) == "invalid comment; missing closing '*/'");
210c5f01b2fSopenharmony_ci        CHECK((scan_string("/**", true) == json::lexer::token_type::parse_error));
211c5f01b2fSopenharmony_ci        CHECK(get_error_message("/**", true) == "invalid comment; missing closing '*/'");
212c5f01b2fSopenharmony_ci
213c5f01b2fSopenharmony_ci        CHECK((scan_string("//", true) == json::lexer::token_type::end_of_input));
214c5f01b2fSopenharmony_ci        CHECK((scan_string("/**/", true) == json::lexer::token_type::end_of_input));
215c5f01b2fSopenharmony_ci        CHECK((scan_string("/** /", true) == json::lexer::token_type::parse_error));
216c5f01b2fSopenharmony_ci        CHECK(get_error_message("/** /", true) == "invalid comment; missing closing '*/'");
217c5f01b2fSopenharmony_ci
218c5f01b2fSopenharmony_ci        CHECK((scan_string("/***/", true) == json::lexer::token_type::end_of_input));
219c5f01b2fSopenharmony_ci        CHECK((scan_string("/* true */", true) == json::lexer::token_type::end_of_input));
220c5f01b2fSopenharmony_ci        CHECK((scan_string("/*/**/", true) == json::lexer::token_type::end_of_input));
221c5f01b2fSopenharmony_ci        CHECK((scan_string("/*/* */", true) == json::lexer::token_type::end_of_input));
222c5f01b2fSopenharmony_ci
223c5f01b2fSopenharmony_ci        CHECK((scan_string("//\n//\n", true) == json::lexer::token_type::end_of_input));
224c5f01b2fSopenharmony_ci        CHECK((scan_string("/**//**//**/", true) == json::lexer::token_type::end_of_input));
225c5f01b2fSopenharmony_ci    }
226c5f01b2fSopenharmony_ci}
227