@ugo-studio/jspp 0.1.2 → 0.1.4
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/README.md +5 -3
- package/dist/analysis/scope.js +38 -15
- package/dist/analysis/typeAnalyzer.js +257 -23
- package/dist/ast/types.js +6 -0
- package/dist/cli.js +3 -4
- package/dist/core/codegen/class-handlers.js +127 -0
- package/dist/core/codegen/control-flow-handlers.js +464 -0
- package/dist/core/codegen/declaration-handlers.js +31 -14
- package/dist/core/codegen/expression-handlers.js +432 -116
- package/dist/core/codegen/function-handlers.js +110 -13
- package/dist/core/codegen/helpers.js +76 -8
- package/dist/core/codegen/index.js +18 -5
- package/dist/core/codegen/literal-handlers.js +3 -0
- package/dist/core/codegen/statement-handlers.js +152 -186
- package/dist/core/codegen/visitor.js +35 -3
- package/package.json +3 -3
- package/src/prelude/any_value.hpp +658 -734
- package/src/prelude/any_value_access.hpp +103 -0
- package/src/prelude/any_value_defines.hpp +151 -0
- package/src/prelude/any_value_helpers.hpp +246 -0
- package/src/prelude/exception.hpp +31 -0
- package/src/prelude/exception_helpers.hpp +49 -0
- package/src/prelude/index.hpp +35 -12
- package/src/prelude/library/console.hpp +20 -20
- package/src/prelude/library/error.hpp +111 -0
- package/src/prelude/library/global.hpp +15 -4
- package/src/prelude/library/performance.hpp +25 -0
- package/src/prelude/library/promise.hpp +121 -0
- package/src/prelude/library/symbol.hpp +60 -4
- package/src/prelude/library/timer.hpp +92 -0
- package/src/prelude/scheduler.hpp +145 -0
- package/src/prelude/types.hpp +33 -6
- package/src/prelude/utils/access.hpp +174 -0
- package/src/prelude/utils/log_any_value/array.hpp +245 -0
- package/src/prelude/utils/log_any_value/config.hpp +32 -0
- package/src/prelude/utils/log_any_value/function.hpp +37 -0
- package/src/prelude/utils/log_any_value/fwd.hpp +15 -0
- package/src/prelude/utils/log_any_value/helpers.hpp +62 -0
- package/src/prelude/utils/log_any_value/log_any_value.hpp +94 -0
- package/src/prelude/utils/log_any_value/object.hpp +119 -0
- package/src/prelude/utils/log_any_value/primitives.hpp +41 -0
- package/src/prelude/{operators.hpp → utils/operators.hpp} +31 -12
- package/src/prelude/utils/well_known_symbols.hpp +13 -0
- package/src/prelude/values/array.hpp +5 -2
- package/src/prelude/{descriptors.hpp → values/descriptors.hpp} +2 -2
- package/src/prelude/values/function.hpp +76 -19
- package/src/prelude/values/{operators → helpers}/array.hpp +30 -14
- package/src/prelude/values/helpers/function.hpp +125 -0
- package/src/prelude/values/helpers/iterator.hpp +107 -0
- package/src/prelude/values/helpers/object.hpp +64 -0
- package/src/prelude/values/helpers/promise.hpp +181 -0
- package/src/prelude/values/helpers/string.hpp +50 -0
- package/src/prelude/values/helpers/symbol.hpp +23 -0
- package/src/prelude/values/iterator.hpp +96 -0
- package/src/prelude/values/object.hpp +8 -3
- package/src/prelude/values/promise.hpp +73 -0
- package/src/prelude/values/prototypes/array.hpp +23 -16
- package/src/prelude/values/prototypes/function.hpp +26 -0
- package/src/prelude/values/prototypes/iterator.hpp +58 -0
- package/src/prelude/values/prototypes/object.hpp +26 -0
- package/src/prelude/values/prototypes/promise.hpp +124 -0
- package/src/prelude/values/prototypes/string.hpp +366 -357
- package/src/prelude/values/prototypes/symbol.hpp +41 -0
- package/src/prelude/values/string.hpp +25 -0
- package/src/prelude/values/symbol.hpp +102 -0
- package/src/prelude/access.hpp +0 -86
- package/src/prelude/error.hpp +0 -31
- package/src/prelude/error_helpers.hpp +0 -59
- package/src/prelude/log_string.hpp +0 -403
- package/src/prelude/values/operators/function.hpp +0 -34
- package/src/prelude/values/operators/object.hpp +0 -34
- package/src/prelude/well_known_symbols.hpp +0 -10
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include <deque>
|
|
3
|
+
#include <functional>
|
|
4
|
+
#include <chrono>
|
|
5
|
+
#include <queue>
|
|
6
|
+
#include <unordered_set>
|
|
7
|
+
#include <vector>
|
|
8
|
+
#include <thread>
|
|
9
|
+
#include <iostream>
|
|
10
|
+
|
|
11
|
+
namespace jspp {
|
|
12
|
+
class Scheduler {
|
|
13
|
+
public:
|
|
14
|
+
using Task = std::function<void()>;
|
|
15
|
+
using TimePoint = std::chrono::steady_clock::time_point;
|
|
16
|
+
|
|
17
|
+
struct Timer {
|
|
18
|
+
size_t id;
|
|
19
|
+
TimePoint next_run;
|
|
20
|
+
std::chrono::milliseconds interval; // 0 if not repeating
|
|
21
|
+
Task task;
|
|
22
|
+
|
|
23
|
+
// Min-heap priority queue (smallest time at top)
|
|
24
|
+
bool operator>(const Timer& other) const {
|
|
25
|
+
return next_run > other.next_run;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
static Scheduler& instance() {
|
|
30
|
+
static Scheduler s;
|
|
31
|
+
return s;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
void enqueue(Task task) {
|
|
35
|
+
tasks.push_back(std::move(task));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
size_t set_timeout(Task task, size_t delay_ms) {
|
|
39
|
+
return schedule_timer(std::move(task), delay_ms, false);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
size_t set_interval(Task task, size_t delay_ms) {
|
|
43
|
+
return schedule_timer(std::move(task), delay_ms, true);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
void clear_timer(size_t id) {
|
|
47
|
+
cancelled_timers.insert(id);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
void run() {
|
|
51
|
+
while (true) {
|
|
52
|
+
bool has_work = false;
|
|
53
|
+
|
|
54
|
+
// 1. Process all immediate tasks (microtask/task queue)
|
|
55
|
+
while (!tasks.empty()) {
|
|
56
|
+
Task task = tasks.front();
|
|
57
|
+
tasks.pop_front();
|
|
58
|
+
task();
|
|
59
|
+
has_work = true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 2. Process timers
|
|
63
|
+
auto now = std::chrono::steady_clock::now();
|
|
64
|
+
while (!timers.empty()) {
|
|
65
|
+
// Peek top
|
|
66
|
+
const auto& top = timers.top();
|
|
67
|
+
|
|
68
|
+
// Cleanup cancelled timers lazily
|
|
69
|
+
if (cancelled_timers.count(top.id)) {
|
|
70
|
+
cancelled_timers.erase(top.id);
|
|
71
|
+
timers.pop();
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (top.next_run <= now) {
|
|
76
|
+
// Timer is ready
|
|
77
|
+
Timer t = top;
|
|
78
|
+
timers.pop();
|
|
79
|
+
|
|
80
|
+
// Execute task
|
|
81
|
+
t.task();
|
|
82
|
+
has_work = true;
|
|
83
|
+
|
|
84
|
+
// Reschedule if interval and not cancelled during execution
|
|
85
|
+
if (t.interval.count() > 0 && cancelled_timers.find(t.id) == cancelled_timers.end()) {
|
|
86
|
+
// Drift-safe(ish) scheduling: run next interval relative to now
|
|
87
|
+
// Note: JS allows drift.
|
|
88
|
+
t.next_run = std::chrono::steady_clock::now() + t.interval;
|
|
89
|
+
timers.push(t);
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
break; // Next timer is in the future
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 3. Exit or Wait
|
|
97
|
+
if (!has_work) {
|
|
98
|
+
if (tasks.empty() && timers.empty()) {
|
|
99
|
+
break; // No pending work, exit event loop
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!timers.empty()) {
|
|
103
|
+
auto next_time = timers.top().next_run;
|
|
104
|
+
std::this_thread::sleep_until(next_time);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
bool has_tasks() const {
|
|
111
|
+
return !tasks.empty() || !timers.empty();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private:
|
|
115
|
+
std::deque<Task> tasks;
|
|
116
|
+
std::priority_queue<Timer, std::vector<Timer>, std::greater<Timer>> timers;
|
|
117
|
+
std::unordered_set<size_t> cancelled_timers;
|
|
118
|
+
size_t next_timer_id = 1;
|
|
119
|
+
const size_t MAX_TIMER_ID = 2147483647; // 2^31 - 1
|
|
120
|
+
|
|
121
|
+
size_t schedule_timer(Task task, size_t delay_ms, bool repeat) {
|
|
122
|
+
size_t id = next_timer_id++;
|
|
123
|
+
if (next_timer_id > MAX_TIMER_ID) {
|
|
124
|
+
next_timer_id = 1;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// If we wrap around, ensure we don't accidentally treat this new ID as cancelled
|
|
128
|
+
// (in case an old timer with this ID is still lingering in the 'cancelled' set)
|
|
129
|
+
if (cancelled_timers.count(id)) {
|
|
130
|
+
cancelled_timers.erase(id);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
auto now = std::chrono::steady_clock::now();
|
|
134
|
+
|
|
135
|
+
Timer t;
|
|
136
|
+
t.id = id;
|
|
137
|
+
t.next_run = now + std::chrono::milliseconds(delay_ms);
|
|
138
|
+
t.interval = repeat ? std::chrono::milliseconds(delay_ms) : std::chrono::milliseconds(0);
|
|
139
|
+
t.task = std::move(task);
|
|
140
|
+
|
|
141
|
+
timers.push(t);
|
|
142
|
+
return id;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
package/src/prelude/types.hpp
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
#include <variant>
|
|
7
7
|
#include <functional>
|
|
8
8
|
#include <memory>
|
|
9
|
+
#include <map>
|
|
9
10
|
#include <unordered_map>
|
|
10
11
|
#include <algorithm>
|
|
11
12
|
#include <cctype>
|
|
@@ -17,34 +18,60 @@
|
|
|
17
18
|
// JSPP standard library
|
|
18
19
|
namespace jspp
|
|
19
20
|
{
|
|
20
|
-
//
|
|
21
|
+
// Js value forward declarations
|
|
21
22
|
struct JsUndefined; // cannot set property
|
|
22
23
|
struct JsNull; // cannot set property
|
|
23
24
|
struct JsUninitialized; // cannot set property
|
|
25
|
+
struct JsString; // can set property
|
|
24
26
|
struct JsObject; // can set property
|
|
25
27
|
struct JsArray; // can set property
|
|
26
28
|
struct JsFunction; // can set property
|
|
29
|
+
struct JsPromise; // can set property
|
|
30
|
+
struct JsSymbol; // can set property (but usually doesn't have own props)
|
|
31
|
+
|
|
32
|
+
template <typename T>
|
|
33
|
+
class JsIterator; // can set property
|
|
27
34
|
|
|
28
35
|
// Object property configuration forward declarations
|
|
29
36
|
struct DataDescriptor;
|
|
30
37
|
struct AccessorDescriptor;
|
|
31
38
|
|
|
39
|
+
// Custom runtime exception
|
|
40
|
+
struct Exception;
|
|
41
|
+
|
|
32
42
|
// Dynamic AnyValue
|
|
33
43
|
class AnyValue;
|
|
34
44
|
|
|
35
|
-
// Custom runtime exception
|
|
36
|
-
struct RuntimeError;
|
|
37
|
-
|
|
38
45
|
// Arithemetic operators
|
|
39
46
|
inline AnyValue pow(const AnyValue &lhs, const AnyValue &rhs);
|
|
40
47
|
|
|
41
48
|
// AnyValue prototypes
|
|
49
|
+
namespace ObjectPrototypes
|
|
50
|
+
{
|
|
51
|
+
inline std::optional<AnyValue> get(const std::string &key, JsObject *self);
|
|
52
|
+
}
|
|
42
53
|
namespace StringPrototypes
|
|
43
54
|
{
|
|
44
|
-
inline std::optional<AnyValue> get(const std::string &key,
|
|
55
|
+
inline std::optional<AnyValue> get(const std::string &key, JsString *self);
|
|
45
56
|
}
|
|
46
57
|
namespace ArrayPrototypes
|
|
47
58
|
{
|
|
48
59
|
inline std::optional<AnyValue> get(const std::string &key, JsArray *self);
|
|
49
60
|
}
|
|
50
|
-
|
|
61
|
+
namespace FunctionPrototypes
|
|
62
|
+
{
|
|
63
|
+
inline std::optional<AnyValue> get(const std::string &key, JsFunction *self);
|
|
64
|
+
}
|
|
65
|
+
namespace PromisePrototypes
|
|
66
|
+
{
|
|
67
|
+
inline std::optional<AnyValue> get(const std::string &key, JsPromise *self);
|
|
68
|
+
}
|
|
69
|
+
namespace IteratorPrototypes
|
|
70
|
+
{
|
|
71
|
+
inline std::optional<AnyValue> get(const std::string &key, JsIterator<AnyValue> *self);
|
|
72
|
+
}
|
|
73
|
+
namespace SymbolPrototypes
|
|
74
|
+
{
|
|
75
|
+
inline std::optional<AnyValue> get(const std::string &key, JsSymbol *self);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "types.hpp"
|
|
4
|
+
#include "well_known_symbols.hpp"
|
|
5
|
+
#include "values/function.hpp"
|
|
6
|
+
#include "values/symbol.hpp"
|
|
7
|
+
#include "exception.hpp"
|
|
8
|
+
#include "any_value.hpp"
|
|
9
|
+
#include <ranges>
|
|
10
|
+
|
|
11
|
+
namespace jspp
|
|
12
|
+
{
|
|
13
|
+
namespace Access
|
|
14
|
+
{
|
|
15
|
+
// Helper function to check for TDZ and deref heap-allocated variables
|
|
16
|
+
inline const AnyValue &deref_ptr(const std::shared_ptr<AnyValue> &var, const std::string &name)
|
|
17
|
+
{
|
|
18
|
+
if ((*var).is_uninitialized()) [[unlikely]]
|
|
19
|
+
{
|
|
20
|
+
Exception::throw_uninitialized_reference(name);
|
|
21
|
+
}
|
|
22
|
+
return *var;
|
|
23
|
+
}
|
|
24
|
+
inline AnyValue &deref_ptr(std::shared_ptr<AnyValue> &var, const std::string &name)
|
|
25
|
+
{
|
|
26
|
+
if ((*var).is_uninitialized()) [[unlikely]]
|
|
27
|
+
{
|
|
28
|
+
Exception::throw_uninitialized_reference(name);
|
|
29
|
+
}
|
|
30
|
+
return *var;
|
|
31
|
+
}
|
|
32
|
+
inline const AnyValue &deref_ptr(const std::unique_ptr<AnyValue> &var, const std::string &name)
|
|
33
|
+
{
|
|
34
|
+
if ((*var).is_uninitialized()) [[unlikely]]
|
|
35
|
+
{
|
|
36
|
+
Exception::throw_uninitialized_reference(name);
|
|
37
|
+
}
|
|
38
|
+
return *var;
|
|
39
|
+
}
|
|
40
|
+
inline AnyValue &deref_ptr(std::unique_ptr<AnyValue> &var, const std::string &name)
|
|
41
|
+
{
|
|
42
|
+
if ((*var).is_uninitialized()) [[unlikely]]
|
|
43
|
+
{
|
|
44
|
+
Exception::throw_uninitialized_reference(name);
|
|
45
|
+
}
|
|
46
|
+
return *var;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Helper function to check for TDZ on stack-allocated variables
|
|
50
|
+
inline const AnyValue &deref_stack(const AnyValue &var, const std::string &name)
|
|
51
|
+
{
|
|
52
|
+
if (var.is_uninitialized()) [[unlikely]]
|
|
53
|
+
{
|
|
54
|
+
Exception::throw_uninitialized_reference(name);
|
|
55
|
+
}
|
|
56
|
+
return var;
|
|
57
|
+
}
|
|
58
|
+
inline AnyValue &deref_stack(AnyValue &var, const std::string &name)
|
|
59
|
+
{
|
|
60
|
+
if (var.is_uninitialized()) [[unlikely]]
|
|
61
|
+
{
|
|
62
|
+
Exception::throw_uninitialized_reference(name);
|
|
63
|
+
}
|
|
64
|
+
return var;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
inline AnyValue typeof(const AnyValue &val)
|
|
68
|
+
{
|
|
69
|
+
switch (val.get_type())
|
|
70
|
+
{
|
|
71
|
+
case JsType::Undefined:
|
|
72
|
+
return AnyValue::make_string("undefined");
|
|
73
|
+
case JsType::Null:
|
|
74
|
+
return AnyValue::make_string("object");
|
|
75
|
+
case JsType::Boolean:
|
|
76
|
+
return AnyValue::make_string("boolean");
|
|
77
|
+
case JsType::Number:
|
|
78
|
+
return AnyValue::make_string("number");
|
|
79
|
+
case JsType::String:
|
|
80
|
+
return AnyValue::make_string("string");
|
|
81
|
+
case JsType::Symbol:
|
|
82
|
+
return AnyValue::make_string("symbol");
|
|
83
|
+
case JsType::Function:
|
|
84
|
+
return AnyValue::make_string("function");
|
|
85
|
+
case JsType::Object:
|
|
86
|
+
case JsType::Array:
|
|
87
|
+
case JsType::Iterator:
|
|
88
|
+
return AnyValue::make_string("object");
|
|
89
|
+
default:
|
|
90
|
+
return AnyValue::make_string("undefined");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
inline AnyValue typeof() // for undeclared variables
|
|
94
|
+
{
|
|
95
|
+
return AnyValue::make_string("undefined");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Helper function to get enumerable own property keys/values of an object
|
|
99
|
+
inline std::vector<std::string> get_object_keys(const AnyValue &obj)
|
|
100
|
+
{
|
|
101
|
+
std::vector<std::string> keys;
|
|
102
|
+
|
|
103
|
+
if (obj.is_object())
|
|
104
|
+
{
|
|
105
|
+
auto ptr = obj.as_object();
|
|
106
|
+
for (const auto &pair : ptr->props)
|
|
107
|
+
{
|
|
108
|
+
if (!JsSymbol::is_internal_key(pair.first))
|
|
109
|
+
keys.push_back(pair.first);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (obj.is_function())
|
|
113
|
+
{
|
|
114
|
+
auto ptr = obj.as_function();
|
|
115
|
+
for (const auto &pair : ptr->props)
|
|
116
|
+
{
|
|
117
|
+
if (!JsSymbol::is_internal_key(pair.first))
|
|
118
|
+
{
|
|
119
|
+
if (!pair.second.is_data_descriptor() && !pair.second.is_accessor_descriptor())
|
|
120
|
+
keys.push_back(pair.first);
|
|
121
|
+
else if ((pair.second.is_data_descriptor() && pair.second.as_data_descriptor()->enumerable) ||
|
|
122
|
+
(pair.second.is_accessor_descriptor() && pair.second.as_accessor_descriptor()->enumerable))
|
|
123
|
+
keys.push_back(pair.first);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (obj.is_array())
|
|
128
|
+
{
|
|
129
|
+
auto len = obj.as_array()->length;
|
|
130
|
+
for (auto i = 0; i < len; ++i)
|
|
131
|
+
{
|
|
132
|
+
keys.push_back(std::to_string(i));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (obj.is_string())
|
|
136
|
+
{
|
|
137
|
+
auto len = obj.as_string()->value.length();
|
|
138
|
+
for (auto i = 0; i < len; ++i)
|
|
139
|
+
{
|
|
140
|
+
keys.push_back(std::to_string(i));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return keys;
|
|
145
|
+
}
|
|
146
|
+
inline AnyValue get_object_value_iterator(const AnyValue &obj, const std::string &name)
|
|
147
|
+
{
|
|
148
|
+
if (obj.is_iterator())
|
|
149
|
+
{
|
|
150
|
+
return obj;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
auto gen_fn = obj.get_own_property(WellKnownSymbols::iterator->key);
|
|
154
|
+
if (gen_fn.is_function())
|
|
155
|
+
{
|
|
156
|
+
auto iter = gen_fn.as_function()->call(gen_fn, {});
|
|
157
|
+
if (iter.is_iterator())
|
|
158
|
+
{
|
|
159
|
+
return iter;
|
|
160
|
+
}
|
|
161
|
+
if (iter.is_object())
|
|
162
|
+
{
|
|
163
|
+
auto next_fn = iter.get_own_property("next");
|
|
164
|
+
if (next_fn.is_function())
|
|
165
|
+
{
|
|
166
|
+
return iter;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
throw jspp::Exception::make_exception(name + " is not iterable", "TypeError");
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include "types.hpp"
|
|
3
|
+
#include "any_value.hpp"
|
|
4
|
+
#include "utils/log_any_value/config.hpp"
|
|
5
|
+
#include "utils/log_any_value/helpers.hpp"
|
|
6
|
+
#include "utils/log_any_value/fwd.hpp"
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <sstream>
|
|
9
|
+
#include <unordered_set>
|
|
10
|
+
#include <algorithm>
|
|
11
|
+
#include <optional>
|
|
12
|
+
|
|
13
|
+
namespace jspp
|
|
14
|
+
{
|
|
15
|
+
namespace LogAnyValue
|
|
16
|
+
{
|
|
17
|
+
inline std::string format_array(const AnyValue &val, std::unordered_set<const void *> &visited, int depth)
|
|
18
|
+
{
|
|
19
|
+
auto arr = val.as_array();
|
|
20
|
+
size_t item_count = static_cast<size_t>(arr->length);
|
|
21
|
+
size_t prop_count = arr->props.size();
|
|
22
|
+
|
|
23
|
+
// // If custom toString exists on the object, prefer it
|
|
24
|
+
// auto itToString = arr->props.find("toString");
|
|
25
|
+
// if (depth > 0 && itToString != arr->props.end() && itToString->second.is_function())
|
|
26
|
+
// {
|
|
27
|
+
// try
|
|
28
|
+
// {
|
|
29
|
+
// auto result = itToString->second.as_function()->call(itToString->second, {});
|
|
30
|
+
// return to_log_string(result, visited, depth);
|
|
31
|
+
// }
|
|
32
|
+
// catch (...)
|
|
33
|
+
// {
|
|
34
|
+
// // ignore and fallback to manual formatting
|
|
35
|
+
// }
|
|
36
|
+
// }
|
|
37
|
+
|
|
38
|
+
std::string indent(depth * 2, ' ');
|
|
39
|
+
std::string next_indent((depth + 1) * 2, ' ');
|
|
40
|
+
std::stringstream ss;
|
|
41
|
+
|
|
42
|
+
// Horizontal layout for small and simple arrays
|
|
43
|
+
bool use_horizontal_layout = item_count <= HORIZONTAL_ARRAY_MAX_ITEMS;
|
|
44
|
+
if (use_horizontal_layout)
|
|
45
|
+
{
|
|
46
|
+
for (size_t i = 0; i < item_count; ++i)
|
|
47
|
+
{
|
|
48
|
+
std::optional<AnyValue> itemVal;
|
|
49
|
+
if (i < arr->dense.size())
|
|
50
|
+
{
|
|
51
|
+
itemVal = arr->dense[i];
|
|
52
|
+
}
|
|
53
|
+
else
|
|
54
|
+
{
|
|
55
|
+
auto it = arr->sparse.find(static_cast<uint32_t>(i));
|
|
56
|
+
if (it != arr->sparse.end())
|
|
57
|
+
{
|
|
58
|
+
itemVal = it->second;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (itemVal.has_value() && !is_simple_value(itemVal.value()))
|
|
62
|
+
{
|
|
63
|
+
use_horizontal_layout = false;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (use_horizontal_layout)
|
|
70
|
+
{
|
|
71
|
+
ss << "[ ";
|
|
72
|
+
size_t empty_count = 0;
|
|
73
|
+
bool needs_comma = false;
|
|
74
|
+
|
|
75
|
+
for (size_t i = 0; i < item_count; ++i)
|
|
76
|
+
{
|
|
77
|
+
std::optional<AnyValue> itemVal;
|
|
78
|
+
if (i < arr->dense.size())
|
|
79
|
+
{
|
|
80
|
+
itemVal = arr->dense[i];
|
|
81
|
+
}
|
|
82
|
+
else
|
|
83
|
+
{
|
|
84
|
+
auto it = arr->sparse.find(static_cast<uint32_t>(i));
|
|
85
|
+
if (it != arr->sparse.end())
|
|
86
|
+
{
|
|
87
|
+
itemVal = it->second;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (itemVal.has_value())
|
|
92
|
+
{
|
|
93
|
+
if (empty_count > 0)
|
|
94
|
+
{
|
|
95
|
+
if (needs_comma)
|
|
96
|
+
ss << Color::BRIGHT_BLACK << ", " << Color::RESET;
|
|
97
|
+
ss << Color::BRIGHT_BLACK << empty_count << " x empty item" << (empty_count > 1 ? "s" : "") << Color::RESET;
|
|
98
|
+
needs_comma = true;
|
|
99
|
+
empty_count = 0;
|
|
100
|
+
}
|
|
101
|
+
if (needs_comma)
|
|
102
|
+
ss << Color::BRIGHT_BLACK << ", " << Color::RESET;
|
|
103
|
+
ss << to_log_string(itemVal.value(), visited, depth + 1);
|
|
104
|
+
needs_comma = true;
|
|
105
|
+
}
|
|
106
|
+
else
|
|
107
|
+
{
|
|
108
|
+
empty_count++;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (empty_count > 0)
|
|
113
|
+
{
|
|
114
|
+
if (needs_comma)
|
|
115
|
+
ss << Color::BRIGHT_BLACK << ", " << Color::RESET;
|
|
116
|
+
ss << Color::BRIGHT_BLACK << empty_count << " x empty item" << (empty_count > 1 ? "s" : "") << Color::RESET;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Print properties
|
|
120
|
+
if (prop_count > 0)
|
|
121
|
+
{
|
|
122
|
+
ss << Color::BRIGHT_BLACK << ", " << Color::RESET;
|
|
123
|
+
|
|
124
|
+
size_t current_prop = 0;
|
|
125
|
+
for (const auto &pair : arr->props)
|
|
126
|
+
{
|
|
127
|
+
if (!is_enumerable_property(pair.second))
|
|
128
|
+
continue;
|
|
129
|
+
|
|
130
|
+
if (is_valid_js_identifier(pair.first))
|
|
131
|
+
{
|
|
132
|
+
ss << pair.first;
|
|
133
|
+
}
|
|
134
|
+
else
|
|
135
|
+
{
|
|
136
|
+
ss << "\"" << pair.first << "\"";
|
|
137
|
+
}
|
|
138
|
+
ss << ": " << to_log_string(pair.second, visited, depth + 1);
|
|
139
|
+
if (++current_prop < prop_count)
|
|
140
|
+
ss << Color::BRIGHT_BLACK << ", " << Color::RESET;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
ss << " ]";
|
|
145
|
+
return ss.str();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Bun-like multi-line layout
|
|
149
|
+
ss << "[\n";
|
|
150
|
+
|
|
151
|
+
const size_t items_to_show = std::min(item_count, MAX_ARRAY_ITEMS);
|
|
152
|
+
size_t empty_count = 0;
|
|
153
|
+
bool first_item_printed = false;
|
|
154
|
+
|
|
155
|
+
for (size_t i = 0; i < items_to_show; ++i)
|
|
156
|
+
{
|
|
157
|
+
std::optional<AnyValue> itemVal;
|
|
158
|
+
if (i < arr->dense.size())
|
|
159
|
+
{
|
|
160
|
+
itemVal = arr->dense[i];
|
|
161
|
+
}
|
|
162
|
+
else
|
|
163
|
+
{
|
|
164
|
+
auto it = arr->sparse.find(static_cast<uint32_t>(i));
|
|
165
|
+
if (it != arr->sparse.end())
|
|
166
|
+
{
|
|
167
|
+
itemVal = it->second;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (itemVal.has_value())
|
|
172
|
+
{
|
|
173
|
+
if (empty_count > 0)
|
|
174
|
+
{
|
|
175
|
+
if (first_item_printed)
|
|
176
|
+
ss << Color::BRIGHT_BLACK << ",\n"
|
|
177
|
+
<< Color::RESET;
|
|
178
|
+
ss << next_indent << Color::BRIGHT_BLACK << empty_count << " x empty item" << (empty_count > 1 ? "s" : "") << Color::RESET;
|
|
179
|
+
first_item_printed = true;
|
|
180
|
+
empty_count = 0;
|
|
181
|
+
}
|
|
182
|
+
if (first_item_printed)
|
|
183
|
+
ss << Color::BRIGHT_BLACK << ",\n"
|
|
184
|
+
<< Color::RESET;
|
|
185
|
+
ss << next_indent << to_log_string(itemVal.value(), visited, depth + 1);
|
|
186
|
+
first_item_printed = true;
|
|
187
|
+
}
|
|
188
|
+
else
|
|
189
|
+
{
|
|
190
|
+
empty_count++;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (empty_count > 0)
|
|
195
|
+
{
|
|
196
|
+
if (first_item_printed)
|
|
197
|
+
ss << Color::BRIGHT_BLACK << ",\n"
|
|
198
|
+
<< Color::RESET;
|
|
199
|
+
ss << next_indent << Color::BRIGHT_BLACK << empty_count << " x empty item" << (empty_count > 1 ? "s" : "") << Color::RESET;
|
|
200
|
+
first_item_printed = true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (item_count > items_to_show)
|
|
204
|
+
{
|
|
205
|
+
if (first_item_printed)
|
|
206
|
+
ss << Color::BRIGHT_BLACK << ",\n"
|
|
207
|
+
<< Color::RESET;
|
|
208
|
+
ss << next_indent << Color::BRIGHT_BLACK << "... " << (item_count - items_to_show) << " more items" << Color::RESET;
|
|
209
|
+
}
|
|
210
|
+
// Print properties
|
|
211
|
+
else if (prop_count > 0)
|
|
212
|
+
{
|
|
213
|
+
if (first_item_printed)
|
|
214
|
+
ss << Color::BRIGHT_BLACK << ",\n"
|
|
215
|
+
<< Color::RESET;
|
|
216
|
+
|
|
217
|
+
size_t current_prop = 0;
|
|
218
|
+
for (const auto &pair : arr->props)
|
|
219
|
+
{
|
|
220
|
+
if (current_prop >= MAX_OBJECT_PROPS)
|
|
221
|
+
break;
|
|
222
|
+
if (!is_enumerable_property(pair.second))
|
|
223
|
+
continue;
|
|
224
|
+
|
|
225
|
+
ss << next_indent;
|
|
226
|
+
if (is_valid_js_identifier(pair.first))
|
|
227
|
+
{
|
|
228
|
+
ss << pair.first;
|
|
229
|
+
}
|
|
230
|
+
else
|
|
231
|
+
{
|
|
232
|
+
ss << "\"" << pair.first << "\"";
|
|
233
|
+
}
|
|
234
|
+
ss << ": " << to_log_string(pair.second, visited, depth + 1);
|
|
235
|
+
if (++current_prop < prop_count)
|
|
236
|
+
ss << Color::BRIGHT_BLACK << ",\n"
|
|
237
|
+
<< Color::RESET;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
ss << "\n";
|
|
241
|
+
ss << indent << "]";
|
|
242
|
+
return ss.str();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <string>
|
|
4
|
+
|
|
5
|
+
namespace jspp
|
|
6
|
+
{
|
|
7
|
+
namespace LogAnyValue
|
|
8
|
+
{
|
|
9
|
+
// --- Configuration for Logging Verbosity ---
|
|
10
|
+
const int MAX_DEPTH = 5;
|
|
11
|
+
const size_t MAX_STRING_LENGTH = 100;
|
|
12
|
+
const size_t MAX_ARRAY_ITEMS = 50;
|
|
13
|
+
const size_t MAX_OBJECT_PROPS = 30;
|
|
14
|
+
|
|
15
|
+
// --- Configuration for Horizontal Layout ---
|
|
16
|
+
const size_t HORIZONTAL_ARRAY_MAX_ITEMS = 10;
|
|
17
|
+
const size_t HORIZONTAL_OBJECT_MAX_PROPS = 5;
|
|
18
|
+
|
|
19
|
+
// ANSI Color Codes for terminal output
|
|
20
|
+
namespace Color
|
|
21
|
+
{
|
|
22
|
+
const std::string RESET = "\033[0m";
|
|
23
|
+
const std::string RED = "\033[31m";
|
|
24
|
+
const std::string GREEN = "\033[32m";
|
|
25
|
+
const std::string YELLOW = "\033[33m";
|
|
26
|
+
const std::string BLUE = "\033[94m";
|
|
27
|
+
const std::string CYAN = "\033[36m";
|
|
28
|
+
const std::string MAGENTA = "\033[35m";
|
|
29
|
+
const std::string BRIGHT_BLACK = "\033[90m"; // Grey
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|