@ugo-studio/jspp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +162 -0
- package/dist/analysis/scope.js +77 -0
- package/dist/analysis/typeAnalyzer.js +224 -0
- package/dist/ast/types.js +1 -0
- package/dist/cli.js +63 -0
- package/dist/core/codegen/declaration-handlers.js +49 -0
- package/dist/core/codegen/expression-handlers.js +333 -0
- package/dist/core/codegen/function-handlers.js +94 -0
- package/dist/core/codegen/helpers.js +83 -0
- package/dist/core/codegen/index.js +54 -0
- package/dist/core/codegen/literal-handlers.js +32 -0
- package/dist/core/codegen/statement-handlers.js +485 -0
- package/dist/core/codegen/visitor.js +86 -0
- package/dist/core/parser.js +6 -0
- package/dist/core/traverser.js +19 -0
- package/dist/index.js +16 -0
- package/package.json +41 -0
- package/src/prelude/access.hpp +86 -0
- package/src/prelude/any_value.hpp +734 -0
- package/src/prelude/descriptors.hpp +25 -0
- package/src/prelude/error.hpp +31 -0
- package/src/prelude/error_helpers.hpp +59 -0
- package/src/prelude/index.hpp +29 -0
- package/src/prelude/library/console.hpp +111 -0
- package/src/prelude/library/global.hpp +10 -0
- package/src/prelude/library/symbol.hpp +8 -0
- package/src/prelude/log_string.hpp +403 -0
- package/src/prelude/operators.hpp +256 -0
- package/src/prelude/types.hpp +50 -0
- package/src/prelude/values/array.hpp +50 -0
- package/src/prelude/values/function.hpp +19 -0
- package/src/prelude/values/non_values.hpp +20 -0
- package/src/prelude/values/object.hpp +17 -0
- package/src/prelude/values/operators/array.hpp +165 -0
- package/src/prelude/values/operators/function.hpp +34 -0
- package/src/prelude/values/operators/object.hpp +34 -0
- package/src/prelude/values/prototypes/array.hpp +228 -0
- package/src/prelude/values/prototypes/function.hpp +0 -0
- package/src/prelude/values/prototypes/object.hpp +0 -0
- package/src/prelude/values/prototypes/string.hpp +357 -0
- package/src/prelude/well_known_symbols.hpp +10 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "types.hpp"
|
|
4
|
+
#include <optional>
|
|
5
|
+
|
|
6
|
+
namespace jspp
|
|
7
|
+
{
|
|
8
|
+
class AnyValue;
|
|
9
|
+
|
|
10
|
+
struct DataDescriptor
|
|
11
|
+
{
|
|
12
|
+
std::shared_ptr<AnyValue> value;
|
|
13
|
+
bool writable = true;
|
|
14
|
+
bool enumerable = false;
|
|
15
|
+
bool configurable = true;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
struct AccessorDescriptor
|
|
19
|
+
{
|
|
20
|
+
std::optional<std::function<AnyValue(const std::vector<AnyValue> &)>> get; // getter = function or undefined
|
|
21
|
+
std::optional<std::function<AnyValue(const std::vector<AnyValue> &)>> set; // setter = function or undefined
|
|
22
|
+
bool enumerable = false;
|
|
23
|
+
bool configurable = true;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
#pragma once
|
|
3
|
+
|
|
4
|
+
#include "types.hpp"
|
|
5
|
+
|
|
6
|
+
namespace jspp
|
|
7
|
+
{
|
|
8
|
+
class AnyValue;
|
|
9
|
+
|
|
10
|
+
struct RuntimeError : std::exception
|
|
11
|
+
{
|
|
12
|
+
std::shared_ptr<AnyValue> data;
|
|
13
|
+
|
|
14
|
+
explicit RuntimeError(std::shared_ptr<AnyValue> d)
|
|
15
|
+
: data(std::move(d)) {}
|
|
16
|
+
explicit RuntimeError(const AnyValue &value)
|
|
17
|
+
: data(std::make_shared<AnyValue>(value)) {}
|
|
18
|
+
explicit RuntimeError(AnyValue &&value)
|
|
19
|
+
: data(std::make_shared<AnyValue>(std::move(value))) {}
|
|
20
|
+
|
|
21
|
+
const char *what() const noexcept override;
|
|
22
|
+
static RuntimeError make_error(const std::string &message, const std::string &name);
|
|
23
|
+
static AnyValue error_to_value(const std::exception &ex);
|
|
24
|
+
|
|
25
|
+
// --- THROWERS
|
|
26
|
+
static AnyValue throw_unresolved_reference_error(const std::string &var_name);
|
|
27
|
+
static AnyValue throw_uninitialized_reference_error(const std::string &var_name);
|
|
28
|
+
static AnyValue throw_immutable_assignment_error();
|
|
29
|
+
static AnyValue throw_invalid_return_statement_error();
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "types.hpp"
|
|
4
|
+
#include "error.hpp"
|
|
5
|
+
#include "any_value.hpp"
|
|
6
|
+
|
|
7
|
+
const char *jspp::RuntimeError::what() const noexcept
|
|
8
|
+
{
|
|
9
|
+
return data->to_std_string().c_str();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
jspp::RuntimeError jspp::RuntimeError::make_error(const std::string &message, const std::string &name = "Error")
|
|
13
|
+
{
|
|
14
|
+
auto errorObj = std::make_shared<AnyValue>(AnyValue::make_object({{"message", AnyValue::make_string(message)}, {"name", AnyValue::make_string(name)}}));
|
|
15
|
+
(*errorObj).set_own_property(WellKnownSymbols::toString, AnyValue::make_function([errorObj](const std::vector<AnyValue> &) -> AnyValue
|
|
16
|
+
{
|
|
17
|
+
AnyValue name = (*errorObj).get_own_property("name");
|
|
18
|
+
AnyValue message = (*errorObj).get_own_property("message");
|
|
19
|
+
std::string str = "";
|
|
20
|
+
if (name.is_string())
|
|
21
|
+
str = name.to_std_string();
|
|
22
|
+
else
|
|
23
|
+
str = "Error";
|
|
24
|
+
str += ": ";
|
|
25
|
+
if (message.is_string())
|
|
26
|
+
str += message.to_std_string();
|
|
27
|
+
return AnyValue::make_string(str); },
|
|
28
|
+
WellKnownSymbols::toString));
|
|
29
|
+
return RuntimeError(errorObj);
|
|
30
|
+
}
|
|
31
|
+
jspp::AnyValue jspp::RuntimeError::error_to_value(const std::exception &ex)
|
|
32
|
+
{
|
|
33
|
+
if (const jspp::RuntimeError *err = dynamic_cast<const jspp::RuntimeError *>(&ex))
|
|
34
|
+
{
|
|
35
|
+
return (*err->data);
|
|
36
|
+
}
|
|
37
|
+
else
|
|
38
|
+
{
|
|
39
|
+
return AnyValue::make_string(ex.what());
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// --- THROWERS
|
|
44
|
+
jspp::AnyValue jspp::RuntimeError::throw_unresolved_reference_error(const std::string &var_name)
|
|
45
|
+
{
|
|
46
|
+
throw RuntimeError::make_error(var_name + " is not defined", "ReferenceError");
|
|
47
|
+
}
|
|
48
|
+
jspp::AnyValue jspp::RuntimeError::throw_uninitialized_reference_error(const std::string &var_name)
|
|
49
|
+
{
|
|
50
|
+
throw RuntimeError::make_error("Cannot access '" + var_name + "' before initialization", "ReferenceError");
|
|
51
|
+
}
|
|
52
|
+
jspp::AnyValue jspp::RuntimeError::throw_immutable_assignment_error()
|
|
53
|
+
{
|
|
54
|
+
throw RuntimeError::make_error("Assignment to constant variable.", "TypeError");
|
|
55
|
+
}
|
|
56
|
+
jspp::AnyValue jspp::RuntimeError::throw_invalid_return_statement_error()
|
|
57
|
+
{
|
|
58
|
+
throw RuntimeError::make_error("Return statements are only valid inside functions.", "SyntaxError");
|
|
59
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "types.hpp"
|
|
4
|
+
#include "well_known_symbols.hpp"
|
|
5
|
+
|
|
6
|
+
// values
|
|
7
|
+
#include "values/non_values.hpp"
|
|
8
|
+
#include "values/object.hpp"
|
|
9
|
+
#include "values/array.hpp"
|
|
10
|
+
#include "values/function.hpp"
|
|
11
|
+
#include "error.hpp"
|
|
12
|
+
#include "descriptors.hpp"
|
|
13
|
+
#include "any_value.hpp"
|
|
14
|
+
#include "error_helpers.hpp"
|
|
15
|
+
#include "values/prototypes/string.hpp"
|
|
16
|
+
#include "values/prototypes/object.hpp"
|
|
17
|
+
#include "values/prototypes/array.hpp"
|
|
18
|
+
#include "values/prototypes/function.hpp"
|
|
19
|
+
#include "values/operators/object.hpp"
|
|
20
|
+
#include "values/operators/array.hpp"
|
|
21
|
+
#include "values/operators/function.hpp"
|
|
22
|
+
|
|
23
|
+
// helpers
|
|
24
|
+
#include "operators.hpp"
|
|
25
|
+
#include "access.hpp"
|
|
26
|
+
#include "log_string.hpp"
|
|
27
|
+
|
|
28
|
+
// js standard libraries
|
|
29
|
+
#include "library/console.hpp"
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "chrono"
|
|
4
|
+
#include "types.hpp"
|
|
5
|
+
#include "values/non_values.hpp"
|
|
6
|
+
#include "values/object.hpp"
|
|
7
|
+
#include "values/function.hpp"
|
|
8
|
+
#include "operators.hpp"
|
|
9
|
+
#include "log_string.hpp"
|
|
10
|
+
|
|
11
|
+
#include <cmath>
|
|
12
|
+
#include <sstream>
|
|
13
|
+
#include <iomanip>
|
|
14
|
+
|
|
15
|
+
static std::unordered_map<std::string, std::chrono::steady_clock::time_point> timers = {};
|
|
16
|
+
|
|
17
|
+
auto logFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue> &args)
|
|
18
|
+
{
|
|
19
|
+
for (size_t i = 0; i < args.size(); ++i)
|
|
20
|
+
{
|
|
21
|
+
std::cout << jspp::LogString::to_log_string(args[i]);
|
|
22
|
+
if (i < args.size() - 1)
|
|
23
|
+
std::cout << " ";
|
|
24
|
+
}
|
|
25
|
+
std::cout << "\n";
|
|
26
|
+
return jspp::AnyValue::make_undefined(); }, "");
|
|
27
|
+
auto warnFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue> &args)
|
|
28
|
+
{
|
|
29
|
+
std::cerr << "\033[33m";
|
|
30
|
+
for (size_t i = 0; i < args.size(); ++i)
|
|
31
|
+
{
|
|
32
|
+
std::cout << jspp::LogString::to_log_string(args[i]);
|
|
33
|
+
if (i < args.size() - 1)
|
|
34
|
+
std::cout << " ";
|
|
35
|
+
}
|
|
36
|
+
std::cerr << "\033[0m" << "\n"; // reset
|
|
37
|
+
return jspp::AnyValue::make_undefined(); }, "");
|
|
38
|
+
auto errorFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue> &args)
|
|
39
|
+
{
|
|
40
|
+
std::cerr << "\033[31m";
|
|
41
|
+
for (size_t i = 0; i < args.size(); ++i)
|
|
42
|
+
{
|
|
43
|
+
std::cout << jspp::LogString::to_log_string(args[i]);
|
|
44
|
+
if (i < args.size() - 1)
|
|
45
|
+
std::cout << " ";
|
|
46
|
+
}
|
|
47
|
+
std::cerr << "\033[0m" << "\n"; // reset
|
|
48
|
+
return jspp::AnyValue::make_undefined(); }, "");
|
|
49
|
+
auto timeFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue> &args)
|
|
50
|
+
{
|
|
51
|
+
auto start = std::chrono::steady_clock::now(); // capture immediately
|
|
52
|
+
auto key_str = args.size() > 0 ? args[0].to_std_string() : "default";
|
|
53
|
+
timers[key_str] = start;
|
|
54
|
+
return jspp::AnyValue::make_undefined(); }, "");
|
|
55
|
+
|
|
56
|
+
// helper to format duration in ms -> ms/s/m/h with nice precision
|
|
57
|
+
static auto format_duration = [](double ms) -> std::string
|
|
58
|
+
{
|
|
59
|
+
std::ostringstream ss;
|
|
60
|
+
if (ms < 1000.0)
|
|
61
|
+
{
|
|
62
|
+
ss << std::fixed << std::setprecision(4) << ms << "ms";
|
|
63
|
+
return ss.str();
|
|
64
|
+
}
|
|
65
|
+
double total_s = ms / 1000.0;
|
|
66
|
+
if (ms < 60000.0)
|
|
67
|
+
{ // less than a minute -> show seconds
|
|
68
|
+
ss << std::fixed << std::setprecision(4) << total_s << "s";
|
|
69
|
+
return ss.str();
|
|
70
|
+
}
|
|
71
|
+
if (ms < 3600000.0)
|
|
72
|
+
{ // less than an hour -> show minutes + seconds
|
|
73
|
+
int minutes = static_cast<int>(ms / 60000.0);
|
|
74
|
+
double seconds = (ms - minutes * 60000.0) / 1000.0;
|
|
75
|
+
ss << minutes << "m " << std::fixed << std::setprecision(4) << seconds << "s";
|
|
76
|
+
return ss.str();
|
|
77
|
+
}
|
|
78
|
+
// hours, minutes, seconds
|
|
79
|
+
int hours = static_cast<int>(ms / 3600000.0);
|
|
80
|
+
int minutes = static_cast<int>((ms - hours * 3600000.0) / 60000.0);
|
|
81
|
+
double seconds = (ms - hours * 3600000.0 - minutes * 60000.0) / 1000.0;
|
|
82
|
+
ss << hours << "h " << minutes << "m " << std::fixed << std::setprecision(4) << seconds << "s";
|
|
83
|
+
return ss.str();
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
auto timeEndFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue> &args)
|
|
87
|
+
{
|
|
88
|
+
auto end = std::chrono::steady_clock::now(); // capture immediately
|
|
89
|
+
auto key_str = args.size() > 0 ? args[0].to_std_string() : "default";
|
|
90
|
+
auto it = timers.find(key_str);
|
|
91
|
+
if (it != timers.end())
|
|
92
|
+
{
|
|
93
|
+
std::chrono::duration<double, std::milli> duration = end - it->second;
|
|
94
|
+
double ms = duration.count();
|
|
95
|
+
std::string formatted = format_duration(ms);
|
|
96
|
+
std::cout << "\033[90m" << "[" << format_duration(ms) << "] " << "\033[0m" << key_str << "\n";
|
|
97
|
+
timers.erase(it); // remove the timer after ending it
|
|
98
|
+
}
|
|
99
|
+
else
|
|
100
|
+
{
|
|
101
|
+
std::cout << "Timer '" << key_str << "' does not exist." << "\n";
|
|
102
|
+
}
|
|
103
|
+
return jspp::AnyValue::make_undefined(); }, "");
|
|
104
|
+
|
|
105
|
+
inline auto console = jspp::AnyValue::make_object({
|
|
106
|
+
{"log", logFn},
|
|
107
|
+
{"warn", warnFn},
|
|
108
|
+
{"error", errorFn},
|
|
109
|
+
{"time", timeFn},
|
|
110
|
+
{"timeEnd", timeEndFn},
|
|
111
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "types.hpp"
|
|
4
|
+
#include "object.hpp"
|
|
5
|
+
#include "well_known_symbols.hpp"
|
|
6
|
+
|
|
7
|
+
inline auto Symbol = jspp::Object::make_object({{"iterator", jspp::Object::make_string(jspp::WellKnownSymbols::iterator)},
|
|
8
|
+
{"toString", jspp::Object::make_string(jspp::WellKnownSymbols::toString)}});
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "types.hpp"
|
|
4
|
+
#include "well_known_symbols.hpp"
|
|
5
|
+
#include "any_value.hpp"
|
|
6
|
+
#include <sstream>
|
|
7
|
+
#include <unordered_set>
|
|
8
|
+
#include <algorithm>
|
|
9
|
+
|
|
10
|
+
namespace jspp
|
|
11
|
+
{
|
|
12
|
+
namespace LogString
|
|
13
|
+
{
|
|
14
|
+
// --- Configuration for Logging Verbosity ---
|
|
15
|
+
const int MAX_DEPTH = 5;
|
|
16
|
+
const size_t MAX_STRING_LENGTH = 100;
|
|
17
|
+
const size_t MAX_ARRAY_ITEMS = 50;
|
|
18
|
+
const size_t MAX_OBJECT_PROPS = 30;
|
|
19
|
+
// --- Configuration for Horizontal Layout ---
|
|
20
|
+
const size_t HORIZONTAL_ARRAY_MAX_ITEMS = 10;
|
|
21
|
+
const size_t HORIZONTAL_OBJECT_MAX_PROPS = 5;
|
|
22
|
+
|
|
23
|
+
// ANSI Color Codes for terminal output
|
|
24
|
+
namespace Color
|
|
25
|
+
{
|
|
26
|
+
const std::string RESET = "\033[0m";
|
|
27
|
+
const std::string GREEN = "\033[32m";
|
|
28
|
+
const std::string YELLOW = "\033[33m";
|
|
29
|
+
const std::string CYAN = "\033[36m";
|
|
30
|
+
const std::string MAGENTA = "\033[35m";
|
|
31
|
+
const std::string BRIGHT_BLACK = "\033[90m"; // Grey
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Forward declarations
|
|
35
|
+
inline std::string to_log_string(const AnyValue &val);
|
|
36
|
+
inline std::string to_log_string(const AnyValue &val, std::unordered_set<const void *> &visited, int depth);
|
|
37
|
+
inline bool is_simple_value(const AnyValue &val);
|
|
38
|
+
|
|
39
|
+
inline bool is_valid_js_identifier(const std::string &s)
|
|
40
|
+
{
|
|
41
|
+
if (s.empty())
|
|
42
|
+
{
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
if (!std::isalpha(s[0]) && s[0] != '_' && s[0] != '$')
|
|
46
|
+
{
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
for (size_t i = 1; i < s.length(); ++i)
|
|
50
|
+
{
|
|
51
|
+
if (!std::isalnum(s[i]) && s[i] != '_' && s[i] != '$')
|
|
52
|
+
{
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
inline bool is_simple_value(const AnyValue &val)
|
|
60
|
+
{
|
|
61
|
+
return val.is_undefined() || val.is_null() || val.is_uninitialized() ||
|
|
62
|
+
val.is_boolean() || val.is_number() || val.is_string();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
inline std::string truncate_string(const std::string &str)
|
|
66
|
+
{
|
|
67
|
+
if (str.length() > MAX_STRING_LENGTH)
|
|
68
|
+
{
|
|
69
|
+
return str.substr(0, MAX_STRING_LENGTH) + "...";
|
|
70
|
+
}
|
|
71
|
+
return str;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
inline std::string to_log_string(const AnyValue &val)
|
|
75
|
+
{
|
|
76
|
+
std::unordered_set<const void *> visited;
|
|
77
|
+
return to_log_string(val, visited, 0);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
inline std::string to_log_string(const AnyValue &val, std::unordered_set<const void *> &visited, int depth)
|
|
81
|
+
{
|
|
82
|
+
// Primitives and simple wrapped values
|
|
83
|
+
if (val.is_uninitialized())
|
|
84
|
+
return Color::BRIGHT_BLACK + std::string("<uninitialized>") + Color::RESET;
|
|
85
|
+
if (val.is_undefined())
|
|
86
|
+
return Color::BRIGHT_BLACK + std::string("undefined") + Color::RESET;
|
|
87
|
+
if (val.is_null())
|
|
88
|
+
return Color::MAGENTA + std::string("null") + Color::RESET;
|
|
89
|
+
if (val.is_boolean())
|
|
90
|
+
return Color::YELLOW + std::string(val.as_boolean() ? "true" : "false") + Color::RESET;
|
|
91
|
+
if (val.is_number())
|
|
92
|
+
return Color::YELLOW + val.to_std_string() + Color::RESET;
|
|
93
|
+
if (val.is_string())
|
|
94
|
+
{
|
|
95
|
+
const std::string &s = *val.as_string();
|
|
96
|
+
if (depth == 0)
|
|
97
|
+
return truncate_string(s);
|
|
98
|
+
return Color::GREEN + std::string("\"") + truncate_string(s) + "\"" + Color::RESET;
|
|
99
|
+
}
|
|
100
|
+
if (val.is_function())
|
|
101
|
+
{
|
|
102
|
+
auto fn = val.as_function();
|
|
103
|
+
auto name_part = fn->name.size() > 0 ? ": " + fn->name : "";
|
|
104
|
+
return Color::CYAN + std::string("[Function") + name_part + "]" + Color::RESET;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Depth limit
|
|
108
|
+
if (depth > MAX_DEPTH)
|
|
109
|
+
{
|
|
110
|
+
if (val.is_object())
|
|
111
|
+
return Color::CYAN + std::string("[Object]") + Color::RESET;
|
|
112
|
+
if (val.is_array())
|
|
113
|
+
return Color::CYAN + std::string("[Array]") + Color::RESET;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Circular reference detection
|
|
117
|
+
const void *ptr_address = nullptr;
|
|
118
|
+
if (val.is_object())
|
|
119
|
+
ptr_address = val.as_object();
|
|
120
|
+
else if (val.is_array())
|
|
121
|
+
ptr_address = val.as_array();
|
|
122
|
+
|
|
123
|
+
if (ptr_address)
|
|
124
|
+
{
|
|
125
|
+
if (visited.count(ptr_address))
|
|
126
|
+
return Color::CYAN + std::string("[Circular]") + Color::RESET;
|
|
127
|
+
visited.insert(ptr_address);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
std::string indent(depth * 2, ' ');
|
|
131
|
+
std::string next_indent((depth + 1) * 2, ' ');
|
|
132
|
+
std::stringstream ss;
|
|
133
|
+
|
|
134
|
+
// Objects
|
|
135
|
+
if (val.is_object())
|
|
136
|
+
{
|
|
137
|
+
auto obj = val.as_object();
|
|
138
|
+
|
|
139
|
+
// If custom toString exists on the object, prefer it
|
|
140
|
+
auto itToString = obj->props.find(jspp::WellKnownSymbols::toString);
|
|
141
|
+
if (itToString != obj->props.end() && itToString->second.is_function())
|
|
142
|
+
{
|
|
143
|
+
try
|
|
144
|
+
{
|
|
145
|
+
auto result = itToString->second.as_function("toString")->call({});
|
|
146
|
+
return to_log_string(result, visited, depth);
|
|
147
|
+
}
|
|
148
|
+
catch (...)
|
|
149
|
+
{
|
|
150
|
+
// ignore and fallback to manual formatting
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
size_t prop_count = obj->props.size();
|
|
155
|
+
|
|
156
|
+
bool use_horizontal_layout = prop_count > 0 && prop_count <= HORIZONTAL_OBJECT_MAX_PROPS;
|
|
157
|
+
if (use_horizontal_layout)
|
|
158
|
+
{
|
|
159
|
+
for (const auto &pair : obj->props)
|
|
160
|
+
{
|
|
161
|
+
if (!is_simple_value(pair.second))
|
|
162
|
+
{
|
|
163
|
+
use_horizontal_layout = false;
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (use_horizontal_layout)
|
|
170
|
+
{
|
|
171
|
+
ss << "{ ";
|
|
172
|
+
size_t current_prop = 0;
|
|
173
|
+
for (const auto &pair : obj->props)
|
|
174
|
+
{
|
|
175
|
+
if (is_valid_js_identifier(pair.first))
|
|
176
|
+
{
|
|
177
|
+
ss << pair.first;
|
|
178
|
+
}
|
|
179
|
+
else
|
|
180
|
+
{
|
|
181
|
+
ss << "\"" << pair.first << "\"";
|
|
182
|
+
}
|
|
183
|
+
ss << ": " << to_log_string(pair.second, visited, depth + 1);
|
|
184
|
+
if (++current_prop < prop_count)
|
|
185
|
+
ss << Color::BRIGHT_BLACK << ", " << Color::RESET;
|
|
186
|
+
}
|
|
187
|
+
ss << " }";
|
|
188
|
+
}
|
|
189
|
+
else
|
|
190
|
+
{
|
|
191
|
+
ss << "{";
|
|
192
|
+
if (prop_count > 0)
|
|
193
|
+
{
|
|
194
|
+
ss << "\n";
|
|
195
|
+
size_t props_shown = 0;
|
|
196
|
+
for (const auto &pair : obj->props)
|
|
197
|
+
{
|
|
198
|
+
if (props_shown >= MAX_OBJECT_PROPS)
|
|
199
|
+
break;
|
|
200
|
+
if (props_shown > 0)
|
|
201
|
+
ss << ",\n";
|
|
202
|
+
|
|
203
|
+
ss << next_indent;
|
|
204
|
+
if (is_valid_js_identifier(pair.first))
|
|
205
|
+
{
|
|
206
|
+
ss << pair.first;
|
|
207
|
+
}
|
|
208
|
+
else
|
|
209
|
+
{
|
|
210
|
+
ss << "\"" << pair.first << "\"";
|
|
211
|
+
}
|
|
212
|
+
ss << ": " << to_log_string(pair.second, visited, depth + 1);
|
|
213
|
+
props_shown++;
|
|
214
|
+
}
|
|
215
|
+
if (prop_count > MAX_OBJECT_PROPS)
|
|
216
|
+
ss << ",\n"
|
|
217
|
+
<< next_indent << Color::BRIGHT_BLACK << "... " << (prop_count - MAX_OBJECT_PROPS) << " more properties" << Color::RESET;
|
|
218
|
+
ss << "\n"
|
|
219
|
+
<< indent;
|
|
220
|
+
}
|
|
221
|
+
ss << "}";
|
|
222
|
+
}
|
|
223
|
+
return ss.str();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Arrays
|
|
227
|
+
if (val.is_array())
|
|
228
|
+
{
|
|
229
|
+
auto arr = val.as_array();
|
|
230
|
+
size_t item_count = static_cast<size_t>(arr->length);
|
|
231
|
+
|
|
232
|
+
// If custom toString exists on the object, prefer it
|
|
233
|
+
auto itToString = arr->props.find(jspp::WellKnownSymbols::toString);
|
|
234
|
+
if (depth > 0 && itToString != arr->props.end() && itToString->second.is_function())
|
|
235
|
+
{
|
|
236
|
+
try
|
|
237
|
+
{
|
|
238
|
+
auto result = itToString->second.as_function("toString")->call({});
|
|
239
|
+
return to_log_string(result, visited, depth);
|
|
240
|
+
}
|
|
241
|
+
catch (...)
|
|
242
|
+
{
|
|
243
|
+
// ignore and fallback to manual formatting
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
std::string indent(depth * 2, ' ');
|
|
248
|
+
std::string next_indent((depth + 1) * 2, ' ');
|
|
249
|
+
std::stringstream ss;
|
|
250
|
+
|
|
251
|
+
// Horizontal layout for small and simple arrays
|
|
252
|
+
bool use_horizontal_layout = item_count <= HORIZONTAL_ARRAY_MAX_ITEMS;
|
|
253
|
+
if (use_horizontal_layout)
|
|
254
|
+
{
|
|
255
|
+
for (size_t i = 0; i < item_count; ++i)
|
|
256
|
+
{
|
|
257
|
+
std::optional<AnyValue> itemVal;
|
|
258
|
+
if (i < arr->dense.size())
|
|
259
|
+
{
|
|
260
|
+
itemVal = arr->dense[i];
|
|
261
|
+
}
|
|
262
|
+
else
|
|
263
|
+
{
|
|
264
|
+
auto it = arr->sparse.find(static_cast<uint32_t>(i));
|
|
265
|
+
if (it != arr->sparse.end())
|
|
266
|
+
{
|
|
267
|
+
itemVal = it->second;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (itemVal.has_value() && !is_simple_value(itemVal.value()))
|
|
271
|
+
{
|
|
272
|
+
use_horizontal_layout = false;
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (use_horizontal_layout)
|
|
279
|
+
{
|
|
280
|
+
ss << "[ ";
|
|
281
|
+
size_t empty_count = 0;
|
|
282
|
+
bool needs_comma = false;
|
|
283
|
+
|
|
284
|
+
for (size_t i = 0; i < item_count; ++i)
|
|
285
|
+
{
|
|
286
|
+
std::optional<AnyValue> itemVal;
|
|
287
|
+
if (i < arr->dense.size())
|
|
288
|
+
{
|
|
289
|
+
itemVal = arr->dense[i];
|
|
290
|
+
}
|
|
291
|
+
else
|
|
292
|
+
{
|
|
293
|
+
auto it = arr->sparse.find(static_cast<uint32_t>(i));
|
|
294
|
+
if (it != arr->sparse.end())
|
|
295
|
+
{
|
|
296
|
+
itemVal = it->second;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (itemVal.has_value())
|
|
301
|
+
{
|
|
302
|
+
if (empty_count > 0)
|
|
303
|
+
{
|
|
304
|
+
if (needs_comma)
|
|
305
|
+
ss << Color::BRIGHT_BLACK << ", " << Color::RESET;
|
|
306
|
+
ss << Color::BRIGHT_BLACK << empty_count << " x empty item" << (empty_count > 1 ? "s" : "") << Color::RESET;
|
|
307
|
+
needs_comma = true;
|
|
308
|
+
empty_count = 0;
|
|
309
|
+
}
|
|
310
|
+
if (needs_comma)
|
|
311
|
+
ss << Color::BRIGHT_BLACK << ", " << Color::RESET;
|
|
312
|
+
ss << to_log_string(itemVal.value(), visited, depth + 1);
|
|
313
|
+
needs_comma = true;
|
|
314
|
+
}
|
|
315
|
+
else
|
|
316
|
+
{
|
|
317
|
+
empty_count++;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (empty_count > 0)
|
|
322
|
+
{
|
|
323
|
+
if (needs_comma)
|
|
324
|
+
ss << Color::BRIGHT_BLACK << ", " << Color::RESET;
|
|
325
|
+
ss << Color::BRIGHT_BLACK << empty_count << " x empty item" << (empty_count > 1 ? "s" : "") << Color::RESET;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
ss << " ]";
|
|
329
|
+
return ss.str();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Bun-like multi-line layout
|
|
333
|
+
ss << "[\n";
|
|
334
|
+
|
|
335
|
+
const size_t items_to_show = std::min(item_count, MAX_ARRAY_ITEMS);
|
|
336
|
+
size_t empty_count = 0;
|
|
337
|
+
bool first_item_printed = false;
|
|
338
|
+
|
|
339
|
+
for (size_t i = 0; i < items_to_show; ++i)
|
|
340
|
+
{
|
|
341
|
+
std::optional<AnyValue> itemVal;
|
|
342
|
+
if (i < arr->dense.size())
|
|
343
|
+
{
|
|
344
|
+
itemVal = arr->dense[i];
|
|
345
|
+
}
|
|
346
|
+
else
|
|
347
|
+
{
|
|
348
|
+
auto it = arr->sparse.find(static_cast<uint32_t>(i));
|
|
349
|
+
if (it != arr->sparse.end())
|
|
350
|
+
{
|
|
351
|
+
itemVal = it->second;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (itemVal.has_value())
|
|
356
|
+
{
|
|
357
|
+
if (empty_count > 0)
|
|
358
|
+
{
|
|
359
|
+
if (first_item_printed)
|
|
360
|
+
ss << Color::BRIGHT_BLACK << ",\n"
|
|
361
|
+
<< Color::RESET;
|
|
362
|
+
ss << next_indent << Color::BRIGHT_BLACK << empty_count << " x empty item" << (empty_count > 1 ? "s" : "") << Color::RESET;
|
|
363
|
+
first_item_printed = true;
|
|
364
|
+
empty_count = 0;
|
|
365
|
+
}
|
|
366
|
+
if (first_item_printed)
|
|
367
|
+
ss << Color::BRIGHT_BLACK << ",\n"
|
|
368
|
+
<< Color::RESET;
|
|
369
|
+
ss << next_indent << to_log_string(itemVal.value(), visited, depth + 1);
|
|
370
|
+
first_item_printed = true;
|
|
371
|
+
}
|
|
372
|
+
else
|
|
373
|
+
{
|
|
374
|
+
empty_count++;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (empty_count > 0)
|
|
379
|
+
{
|
|
380
|
+
if (first_item_printed)
|
|
381
|
+
ss << Color::BRIGHT_BLACK << ",\n"
|
|
382
|
+
<< Color::RESET;
|
|
383
|
+
ss << next_indent << Color::BRIGHT_BLACK << empty_count << " x empty item" << (empty_count > 1 ? "s" : "") << Color::RESET;
|
|
384
|
+
first_item_printed = true;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (item_count > items_to_show)
|
|
388
|
+
{
|
|
389
|
+
if (first_item_printed)
|
|
390
|
+
ss << Color::BRIGHT_BLACK << ",\n"
|
|
391
|
+
<< Color::RESET;
|
|
392
|
+
ss << next_indent << Color::BRIGHT_BLACK << "... " << (item_count - items_to_show) << " more items" << Color::RESET;
|
|
393
|
+
}
|
|
394
|
+
ss << "\n";
|
|
395
|
+
ss << indent << "]";
|
|
396
|
+
return ss.str();
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Fallback
|
|
400
|
+
return val.to_std_string();
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|