Skip to content

Commit 974b0d7

Browse files
author
Fszontagh
committed
refactor(JsonModule): Replace custom JSON implementation with nlohmann JSON library
1 parent 4f7e640 commit 974b0d7

File tree

7 files changed

+26350
-312
lines changed

7 files changed

+26350
-312
lines changed

CMakeLists.txt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ add_library(voidscript
142142
src/Symbols/Value.cpp
143143
src/Symbols/EnumSymbol.cpp
144144
src/Modules/BuiltIn/ModuleHelperModule.cpp
145+
src/Modules/BuiltIn/JsonConverters.cpp
146+
src/Modules/BuiltIn/JsonModule.cpp
145147
src/Interpreter/Interpreter.cpp
146148
src/Compiler/VoidScriptCompiler.cpp
147149
src/Compiler/CompilerBackend.cpp
@@ -193,12 +195,12 @@ endif()
193195
# COMPILER TARGET
194196
if (BUILD_COMPILER)
195197
add_subdirectory(compiler)
196-
198+
197199
set(CPACK_DEBIAN_COMPILER_FILE_NAME "${CMAKE_PROJECT_NAME}-compiler_${CMAKE_PROJECT_VERSION}_${ARCHITECTURE}_${CPACK_SYSTEM_NAME}.deb")
198200
set(CPACK_DEBIAN_COMPILER_PACKAGE_NAME "${CMAKE_PROJECT_NAME}-compiler")
199201
set(CPACK_DEBIAN_COMPILER_PACKAGE_SECTION "devel")
200202
set(CPACK_DEBIAN_COMPILER_PACKAGE_DEPENDS "libvoidscript (= ${CMAKE_PROJECT_VERSION})")
201-
203+
202204
set(CPACK_RPM_COMPILER_FILE_NAME "${CMAKE_PROJECT_NAME}-compiler_${CMAKE_PROJECT_VERSION}_${ARCHITECTURE}_${CPACK_SYSTEM_NAME}.rpm")
203205
set(CPACK_RPM_COMPILER_PACKAGE_NAME "${CMAKE_PROJECT_NAME}-compiler")
204206
set(CPACK_RPM_COMPILER_PACKAGE_GROUP "Development/Tools")
@@ -328,7 +330,7 @@ if (BUILD_TESTS)
328330
# Add more tests here if needed...
329331

330332
endif()
331-
333+
332334
# Add compiler tests if compiler is enabled
333335
if(BUILD_COMPILER)
334336
add_subdirectory(tests/compiler)
Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
// JsonConverters.cpp
2+
#include "JsonConverters.hpp"
3+
#include "../../json.hpp"
4+
#include <sstream>
5+
#include <iomanip>
6+
#include <cmath>
7+
#include <limits>
8+
9+
namespace Modules {
10+
namespace JsonConverters {
11+
12+
namespace {
13+
14+
/**
15+
* @brief Helper function to create detailed error messages
16+
*
17+
* @param operation The operation being performed (e.g., "ValuePtr to JSON")
18+
* @param valueType The type of the ValuePtr
19+
* @param context Additional context information
20+
* @return std::string Detailed error message
21+
*/
22+
std::string createErrorMessage(const std::string& operation,
23+
Symbols::Variables::Type valueType,
24+
const std::string& context = "") {
25+
std::string typeStr = Symbols::Variables::TypeToString(valueType);
26+
std::string msg = "Conversion error in " + operation +
27+
": unsupported type '" + typeStr + "'";
28+
29+
if (!context.empty()) {
30+
msg += " in context: " + context;
31+
}
32+
33+
return msg;
34+
}
35+
36+
/**
37+
* @brief Helper function to create detailed error messages for JSON conversion
38+
*
39+
* @param operation The operation being performed (e.g., "JSON to ValuePtr")
40+
* @param jsonType The type of the JSON value
41+
* @param context Additional context information
42+
* @return std::string Detailed error message
43+
*/
44+
std::string createJsonErrorMessage(const std::string& operation,
45+
const std::string& jsonType,
46+
const std::string& context = "") {
47+
std::string msg = "Conversion error in " + operation +
48+
": unsupported JSON type '" + jsonType + "'";
49+
50+
if (!context.empty()) {
51+
msg += " in context: " + context;
52+
}
53+
54+
return msg;
55+
}
56+
57+
/**
58+
* @brief Converts a JSON number to the appropriate VoidScript numeric type
59+
*
60+
* @param json The JSON number value
61+
* @return Symbols::ValuePtr The converted numeric ValuePtr
62+
*/
63+
Symbols::ValuePtr convertJsonNumberToValue(const nlohmann::json& json) {
64+
if (json.is_number_integer()) {
65+
return Symbols::ValuePtr(static_cast<int>(json.get<int64_t>()));
66+
} else if (json.is_number_unsigned()) {
67+
return Symbols::ValuePtr(static_cast<int>(json.get<uint64_t>()));
68+
} else if (json.is_number_float()) {
69+
double value = json.get<double>();
70+
// Check if it can be represented as a float without losing precision
71+
if (value >= std::numeric_limits<float>::min() &&
72+
value <= std::numeric_limits<float>::max() &&
73+
std::abs(value) <= std::numeric_limits<float>::max()) {
74+
return Symbols::ValuePtr(static_cast<float>(value));
75+
}
76+
return Symbols::ValuePtr(value);
77+
}
78+
79+
throw std::runtime_error("Invalid JSON number type");
80+
}
81+
82+
/**
83+
* @brief Recursively converts a JSON object to VoidScript ObjectMap
84+
*
85+
* @param json The JSON object to convert
86+
* @return Symbols::ObjectMap The converted ObjectMap
87+
*/
88+
Symbols::ObjectMap convertJsonObjectToMap(const nlohmann::json& json) {
89+
Symbols::ObjectMap result;
90+
91+
for (auto it = json.begin(); it != json.end(); ++it) {
92+
const std::string& key = it.key();
93+
const nlohmann::json& value = it.value();
94+
95+
if (value.is_null()) {
96+
result[key] = Symbols::ValuePtr::null(Symbols::Variables::Type::NULL_TYPE);
97+
} else if (value.is_boolean()) {
98+
result[key] = Symbols::ValuePtr(value.get<bool>());
99+
} else if (value.is_number_integer()) {
100+
result[key] = Symbols::ValuePtr(static_cast<int>(value.get<int64_t>()));
101+
} else if (value.is_number_unsigned()) {
102+
result[key] = Symbols::ValuePtr(static_cast<int>(value.get<uint64_t>()));
103+
} else if (value.is_number_float()) {
104+
result[key] = Symbols::ValuePtr(value.get<double>());
105+
} else if (value.is_string()) {
106+
result[key] = Symbols::ValuePtr(value.get<std::string>());
107+
} else if (value.is_array()) {
108+
// Convert JSON array to ObjectMap with numeric string keys
109+
Symbols::ObjectMap arrayMap;
110+
for (size_t i = 0; i < value.size(); ++i) {
111+
const std::string indexKey = std::to_string(i);
112+
const nlohmann::json& element = value[i];
113+
114+
if (element.is_null()) {
115+
arrayMap[indexKey] = Symbols::ValuePtr::null(Symbols::Variables::Type::NULL_TYPE);
116+
} else if (element.is_boolean()) {
117+
arrayMap[indexKey] = Symbols::ValuePtr(element.get<bool>());
118+
} else if (element.is_number_integer()) {
119+
arrayMap[indexKey] = Symbols::ValuePtr(static_cast<int>(element.get<int64_t>()));
120+
} else if (element.is_number_unsigned()) {
121+
arrayMap[indexKey] = Symbols::ValuePtr(static_cast<int>(element.get<uint64_t>()));
122+
} else if (element.is_number_float()) {
123+
arrayMap[indexKey] = Symbols::ValuePtr(element.get<double>());
124+
} else if (element.is_string()) {
125+
arrayMap[indexKey] = Symbols::ValuePtr(element.get<std::string>());
126+
} else if (element.is_object()) {
127+
arrayMap[indexKey] = Symbols::ValuePtr(convertJsonObjectToMap(element));
128+
} else if (element.is_array()) {
129+
arrayMap[indexKey] = Symbols::ValuePtr(convertJsonObjectToMap(element));
130+
}
131+
}
132+
result[key] = Symbols::ValuePtr(arrayMap);
133+
} else if (value.is_object()) {
134+
result[key] = Symbols::ValuePtr(convertJsonObjectToMap(value));
135+
}
136+
}
137+
138+
return result;
139+
}
140+
141+
/**
142+
* @brief Recursively converts a VoidScript ObjectMap to JSON object
143+
*
144+
* @param objMap The ObjectMap to convert
145+
* @return nlohmann::json The converted JSON object
146+
*/
147+
nlohmann::json convertMapToJson(const Symbols::ObjectMap& objMap) {
148+
nlohmann::json result;
149+
150+
for (const auto& kv : objMap) {
151+
const std::string& key = kv.first;
152+
const Symbols::ValuePtr& value = kv.second;
153+
154+
switch (value.getType()) {
155+
case Symbols::Variables::Type::NULL_TYPE:
156+
result[key] = nullptr;
157+
break;
158+
159+
case Symbols::Variables::Type::BOOLEAN:
160+
result[key] = value.get<bool>();
161+
break;
162+
163+
case Symbols::Variables::Type::INTEGER:
164+
result[key] = value.get<int>();
165+
break;
166+
167+
case Symbols::Variables::Type::FLOAT:
168+
result[key] = value.get<float>();
169+
break;
170+
171+
case Symbols::Variables::Type::DOUBLE:
172+
result[key] = value.get<double>();
173+
break;
174+
175+
case Symbols::Variables::Type::STRING:
176+
result[key] = value.get<std::string>();
177+
break;
178+
179+
case Symbols::Variables::Type::OBJECT:
180+
case Symbols::Variables::Type::CLASS:
181+
result[key] = convertMapToJson(value.get<Symbols::ObjectMap>());
182+
break;
183+
184+
case Symbols::Variables::Type::ENUM:
185+
// Convert enum to string representation
186+
result[key] = value.toString();
187+
break;
188+
189+
default:
190+
// Unsupported type, convert to null
191+
result[key] = nullptr;
192+
break;
193+
}
194+
}
195+
196+
return result;
197+
}
198+
199+
} // anonymous namespace
200+
201+
nlohmann::json valueToJson(const Symbols::ValuePtr& value) {
202+
if (!value) {
203+
throw std::runtime_error("Cannot convert null ValuePtr to JSON");
204+
}
205+
206+
return valueToJsonWithContext(value, "");
207+
}
208+
209+
Symbols::ValuePtr jsonToValue(const nlohmann::json& json) {
210+
return jsonToValueWithContext(json, "");
211+
}
212+
213+
nlohmann::json valueToJsonWithContext(const Symbols::ValuePtr& value, const std::string& context) {
214+
if (!value) {
215+
throw std::runtime_error(createErrorMessage("ValuePtr to JSON conversion",
216+
Symbols::Variables::Type::NULL_TYPE, context));
217+
}
218+
219+
Symbols::Variables::Type type = value.getType();
220+
221+
switch (type) {
222+
case Symbols::Variables::Type::NULL_TYPE:
223+
return nullptr;
224+
225+
case Symbols::Variables::Type::BOOLEAN:
226+
return value.get<bool>();
227+
228+
case Symbols::Variables::Type::INTEGER:
229+
return value.get<int>();
230+
231+
case Symbols::Variables::Type::FLOAT:
232+
return value.get<float>();
233+
234+
case Symbols::Variables::Type::DOUBLE:
235+
return value.get<double>();
236+
237+
case Symbols::Variables::Type::STRING:
238+
return value.get<std::string>();
239+
240+
case Symbols::Variables::Type::OBJECT:
241+
case Symbols::Variables::Type::CLASS:
242+
return convertMapToJson(value.get<Symbols::ObjectMap>());
243+
244+
case Symbols::Variables::Type::ENUM:
245+
return value.toString();
246+
247+
default:
248+
throw std::runtime_error(createErrorMessage("ValuePtr to JSON conversion", type, context));
249+
}
250+
}
251+
252+
Symbols::ValuePtr jsonToValueWithContext(const nlohmann::json& json, const std::string& context) {
253+
if (json.is_null()) {
254+
return Symbols::ValuePtr::null(Symbols::Variables::Type::NULL_TYPE);
255+
} else if (json.is_boolean()) {
256+
return Symbols::ValuePtr(json.get<bool>());
257+
} else if (json.is_number_integer()) {
258+
return Symbols::ValuePtr(static_cast<int>(json.get<int64_t>()));
259+
} else if (json.is_number_unsigned()) {
260+
return Symbols::ValuePtr(static_cast<int>(json.get<uint64_t>()));
261+
} else if (json.is_number_float()) {
262+
return Symbols::ValuePtr(json.get<double>());
263+
} else if (json.is_string()) {
264+
return Symbols::ValuePtr(json.get<std::string>());
265+
} else if (json.is_array()) {
266+
// Convert JSON array to ObjectMap with numeric string keys
267+
Symbols::ObjectMap arrayMap;
268+
for (size_t i = 0; i < json.size(); ++i) {
269+
const std::string indexKey = std::to_string(i);
270+
const nlohmann::json& element = json[i];
271+
272+
if (element.is_null()) {
273+
arrayMap[indexKey] = Symbols::ValuePtr::null(Symbols::Variables::Type::NULL_TYPE);
274+
} else if (element.is_boolean()) {
275+
arrayMap[indexKey] = Symbols::ValuePtr(element.get<bool>());
276+
} else if (element.is_number_integer()) {
277+
arrayMap[indexKey] = Symbols::ValuePtr(static_cast<int>(element.get<int64_t>()));
278+
} else if (element.is_number_unsigned()) {
279+
arrayMap[indexKey] = Symbols::ValuePtr(static_cast<int>(element.get<uint64_t>()));
280+
} else if (element.is_number_float()) {
281+
arrayMap[indexKey] = Symbols::ValuePtr(element.get<double>());
282+
} else if (element.is_string()) {
283+
arrayMap[indexKey] = Symbols::ValuePtr(element.get<std::string>());
284+
} else if (element.is_object()) {
285+
arrayMap[indexKey] = Symbols::ValuePtr(convertJsonObjectToMap(element));
286+
} else if (element.is_array()) {
287+
arrayMap[indexKey] = Symbols::ValuePtr(convertJsonObjectToMap(element));
288+
}
289+
}
290+
return Symbols::ValuePtr(arrayMap);
291+
} else if (json.is_object()) {
292+
return Symbols::ValuePtr(convertJsonObjectToMap(json));
293+
} else {
294+
throw std::runtime_error(createJsonErrorMessage("JSON to ValuePtr conversion",
295+
"unknown", context));
296+
}
297+
}
298+
299+
bool canConvertToJson(const Symbols::ValuePtr& value) {
300+
if (!value) {
301+
return false;
302+
}
303+
304+
Symbols::Variables::Type type = value.getType();
305+
306+
switch (type) {
307+
case Symbols::Variables::Type::NULL_TYPE:
308+
case Symbols::Variables::Type::BOOLEAN:
309+
case Symbols::Variables::Type::INTEGER:
310+
case Symbols::Variables::Type::FLOAT:
311+
case Symbols::Variables::Type::DOUBLE:
312+
case Symbols::Variables::Type::STRING:
313+
case Symbols::Variables::Type::OBJECT:
314+
case Symbols::Variables::Type::CLASS:
315+
case Symbols::Variables::Type::ENUM:
316+
return true;
317+
318+
default:
319+
return false;
320+
}
321+
}
322+
323+
bool canConvertToValue(const nlohmann::json& json) {
324+
return json.is_null() || json.is_boolean() || json.is_number() ||
325+
json.is_string() || json.is_array() || json.is_object();
326+
}
327+
328+
} // namespace JsonConverters
329+
} // namespace Modules

0 commit comments

Comments
 (0)