@ugo-studio/jspp 0.1.2 → 0.1.3

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.
Files changed (41) hide show
  1. package/README.md +3 -1
  2. package/dist/analysis/scope.js +22 -11
  3. package/dist/analysis/typeAnalyzer.js +4 -3
  4. package/dist/cli.js +2 -2
  5. package/dist/core/codegen/expression-handlers.js +10 -6
  6. package/dist/core/codegen/function-handlers.js +27 -6
  7. package/dist/core/codegen/helpers.js +12 -8
  8. package/dist/core/codegen/index.js +3 -1
  9. package/dist/core/codegen/statement-handlers.js +53 -16
  10. package/dist/core/codegen/visitor.js +7 -1
  11. package/package.json +1 -1
  12. package/src/prelude/access.hpp +8 -3
  13. package/src/prelude/any_value.hpp +141 -241
  14. package/src/prelude/any_value_helpers.hpp +225 -0
  15. package/src/prelude/error_helpers.hpp +3 -3
  16. package/src/prelude/index.hpp +17 -3
  17. package/src/prelude/library/console.hpp +7 -7
  18. package/src/prelude/library/performance.hpp +25 -0
  19. package/src/prelude/library/symbol.hpp +60 -3
  20. package/src/prelude/log_string.hpp +10 -6
  21. package/src/prelude/operators.hpp +2 -2
  22. package/src/prelude/types.hpp +24 -6
  23. package/src/prelude/values/array.hpp +2 -0
  24. package/src/prelude/values/function.hpp +33 -1
  25. package/src/prelude/values/{operators → helpers}/array.hpp +14 -7
  26. package/src/prelude/values/helpers/function.hpp +77 -0
  27. package/src/prelude/values/helpers/iterator.hpp +101 -0
  28. package/src/prelude/values/helpers/string.hpp +50 -0
  29. package/src/prelude/values/helpers/symbol.hpp +23 -0
  30. package/src/prelude/values/iterator.hpp +88 -0
  31. package/src/prelude/values/object.hpp +2 -1
  32. package/src/prelude/values/prototypes/array.hpp +18 -11
  33. package/src/prelude/values/prototypes/function.hpp +26 -0
  34. package/src/prelude/values/prototypes/iterator.hpp +57 -0
  35. package/src/prelude/values/prototypes/string.hpp +366 -357
  36. package/src/prelude/values/prototypes/symbol.hpp +39 -0
  37. package/src/prelude/values/string.hpp +25 -0
  38. package/src/prelude/values/symbol.hpp +102 -0
  39. package/src/prelude/well_known_symbols.hpp +7 -3
  40. package/src/prelude/values/operators/function.hpp +0 -34
  41. /package/src/prelude/values/{operators → helpers}/object.hpp +0 -0
@@ -47,6 +47,16 @@ std::string jspp::JsArray::to_std_string() const
47
47
  return result;
48
48
  }
49
49
 
50
+ jspp::JsIterator<jspp::AnyValue> jspp::JsArray::get_iterator()
51
+ {
52
+ for (uint64_t idx = 0; idx < length; ++idx)
53
+ {
54
+ co_yield get_property(static_cast<uint32_t>(idx));
55
+ }
56
+
57
+ co_return AnyValue::make_undefined();
58
+ }
59
+
50
60
  jspp::AnyValue jspp::JsArray::get_property(const std::string &key)
51
61
  {
52
62
  if (
@@ -100,14 +110,11 @@ jspp::AnyValue jspp::JsArray::set_property(const std::string &key, const AnyValu
100
110
  else
101
111
  {
102
112
  // set prototype property if accessor descriptor
103
- auto proto_it = ArrayPrototypes::get(key, this);
104
- if (proto_it.has_value())
113
+ auto proto_val_opt = ArrayPrototypes::get(key, this);
114
+ if (proto_val_opt.has_value())
105
115
  {
106
- auto proto_value = proto_it.value();
107
- if (proto_value.is_accessor_descriptor())
108
- {
109
- return AnyValue::resolve_property_for_write(proto_it.value(), value);
110
- }
116
+ auto proto_value = proto_val_opt.value();
117
+ return AnyValue::resolve_property_for_write(proto_value, value);
111
118
  }
112
119
 
113
120
  // set own property
@@ -0,0 +1,77 @@
1
+ #pragma once
2
+
3
+ #include <variant>
4
+
5
+ #include "types.hpp"
6
+ #include "values/function.hpp"
7
+ #include "any_value.hpp"
8
+ #include "values/prototypes/function.hpp"
9
+
10
+ std::string jspp::JsFunction::to_std_string() const
11
+ {
12
+ if (is_generator)
13
+ {
14
+ return "function* " + name + "() { [native code] }";
15
+ }
16
+ return "function " + name + "() { [native code] }";
17
+ }
18
+
19
+ jspp::AnyValue jspp::JsFunction::call(const std::vector<AnyValue> &args)
20
+ {
21
+
22
+ if (std::function<AnyValue(const std::vector<AnyValue> &)> *func = std::get_if<0>(&callable))
23
+ {
24
+ return (*func)(args);
25
+ }
26
+ else if (std::function<jspp::JsIterator<jspp::AnyValue>(const std::vector<jspp::AnyValue> &)> *func = std::get_if<1>(&callable))
27
+ {
28
+ return AnyValue::from_iterator((*func)(args));
29
+ }
30
+ else
31
+ {
32
+ return AnyValue::make_undefined();
33
+ }
34
+ }
35
+
36
+ jspp::AnyValue jspp::JsFunction::get_property(const std::string &key)
37
+ {
38
+ auto it = props.find(key);
39
+ if (it == props.end())
40
+ {
41
+ // check prototype
42
+ auto proto_it = FunctionPrototypes::get(key, this);
43
+ if (proto_it.has_value())
44
+ {
45
+ return AnyValue::resolve_property_for_read(proto_it.value());
46
+ }
47
+ // not found
48
+ return AnyValue::make_undefined();
49
+ }
50
+ return AnyValue::resolve_property_for_read(it->second);
51
+ }
52
+
53
+ jspp::AnyValue jspp::JsFunction::set_property(const std::string &key, const AnyValue &value)
54
+ {
55
+ // set prototype property if accessor descriptor
56
+ auto proto_it = FunctionPrototypes::get(key, this);
57
+ if (proto_it.has_value())
58
+ {
59
+ auto proto_value = proto_it.value();
60
+ if (proto_value.is_accessor_descriptor())
61
+ {
62
+ return AnyValue::resolve_property_for_write(proto_value, value);
63
+ }
64
+ }
65
+
66
+ // set own property
67
+ auto it = props.find(key);
68
+ if (it != props.end())
69
+ {
70
+ return AnyValue::resolve_property_for_write(it->second, value);
71
+ }
72
+ else
73
+ {
74
+ props[key] = value;
75
+ return value;
76
+ }
77
+ }
@@ -0,0 +1,101 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "values/iterator.hpp"
5
+ #include "any_value.hpp"
6
+
7
+ template <typename T>
8
+ std::string jspp::JsIterator<T>::to_std_string() const
9
+ {
10
+ return "[object Generator]";
11
+ }
12
+
13
+ template <typename T>
14
+ jspp::JsIterator<T>::NextResult jspp::JsIterator<T>::next()
15
+ {
16
+ // If the generator is already finished or invalid, return {undefined, true}
17
+ if (!handle || handle.done())
18
+ return {std::nullopt, true};
19
+
20
+ // Resume execution until next co_yield or co_return
21
+ handle.resume();
22
+
23
+ if (handle.promise().exception_)
24
+ {
25
+ std::rethrow_exception(handle.promise().exception_);
26
+ }
27
+
28
+ // If handle.done() is TRUE, we hit co_return (value: X, done: true)
29
+ // If handle.done() is FALSE, we hit co_yield (value: X, done: false)
30
+ bool is_done = handle.done();
31
+
32
+ return {handle.promise().current_value, is_done};
33
+ }
34
+
35
+ template <typename T>
36
+ std::vector<std::optional<T>> jspp::JsIterator<T>::to_vector()
37
+ {
38
+ std::vector<std::optional<AnyValue>> result;
39
+ while (true)
40
+ {
41
+ auto next = this->next();
42
+ if (next.done)
43
+ {
44
+ break;
45
+ }
46
+ result.push_back(next.value);
47
+ }
48
+ return result;
49
+ }
50
+
51
+ template <typename T>
52
+ jspp::AnyValue jspp::JsIterator<T>::get_property(const std::string &key)
53
+ {
54
+ auto it = props.find(key);
55
+ if (it == props.end())
56
+ {
57
+ // check prototype
58
+ if constexpr (std::is_same_v<T, AnyValue>)
59
+ {
60
+ auto proto_it = IteratorPrototypes::get(key, this);
61
+ if (proto_it.has_value())
62
+ {
63
+ return AnyValue::resolve_property_for_read(proto_it.value());
64
+ }
65
+ }
66
+ // prototype not found
67
+ return jspp::AnyValue::make_undefined();
68
+ }
69
+
70
+ return jspp::AnyValue::resolve_property_for_read(it->second);
71
+ }
72
+
73
+ template <typename T>
74
+ jspp::AnyValue jspp::JsIterator<T>::set_property(const std::string &key, const AnyValue &value)
75
+ {
76
+ // set prototype property if accessor descriptor
77
+ if constexpr (std::is_same_v<T, AnyValue>)
78
+ {
79
+ auto proto_it = IteratorPrototypes::get(key, this);
80
+ if (proto_it.has_value())
81
+ {
82
+ auto proto_value = proto_it.value();
83
+ if (proto_value.is_accessor_descriptor())
84
+ {
85
+ return AnyValue::resolve_property_for_write(proto_it.value(), value);
86
+ }
87
+ }
88
+ }
89
+
90
+ // set own property
91
+ auto it = props.find(key);
92
+ if (it != props.end())
93
+ {
94
+ return jspp::AnyValue::resolve_property_for_write(it->second, value);
95
+ }
96
+ else
97
+ {
98
+ props[key] = value;
99
+ return value;
100
+ }
101
+ }
@@ -0,0 +1,50 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "values/array.hpp"
5
+ #include "values/string.hpp"
6
+ #include "error.hpp"
7
+ #include "any_value.hpp"
8
+ #include "values/prototypes/string.hpp"
9
+
10
+ std::string jspp::JsString::to_std_string() const
11
+ {
12
+ return value;
13
+ }
14
+
15
+ jspp::JsIterator<jspp::AnyValue> jspp::JsString::get_iterator()
16
+ {
17
+ size_t strLength = value.length();
18
+ for (size_t idx = 0; idx < strLength; idx++)
19
+ {
20
+ co_yield AnyValue::make_string(std::string(1, value[idx]));
21
+ }
22
+ co_return AnyValue::make_undefined();
23
+ }
24
+
25
+ jspp::AnyValue jspp::JsString::get_property(const std::string &key)
26
+ {
27
+ // Check for prototype methods
28
+ auto proto_fn = StringPrototypes::get(key, this);
29
+ if (proto_fn.has_value())
30
+ {
31
+ return AnyValue::resolve_property_for_read(proto_fn.value());
32
+ }
33
+ // Handle character access by string index (e.g., "abc"["1"])
34
+ if (JsArray::is_array_index(key))
35
+ {
36
+ uint32_t idx = static_cast<uint32_t>(std::stoull(key));
37
+ return get_property(idx);
38
+ }
39
+ // not found
40
+ return AnyValue::make_undefined();
41
+ }
42
+
43
+ jspp::AnyValue jspp::JsString::get_property(uint32_t idx)
44
+ {
45
+ if (idx < value.length())
46
+ {
47
+ return AnyValue::make_string(std::string(1, value[idx]));
48
+ }
49
+ return AnyValue::make_undefined();
50
+ }
@@ -0,0 +1,23 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "values/symbol.hpp"
5
+ #include "any_value.hpp"
6
+ #include "values/prototypes/symbol.hpp"
7
+
8
+ std::string jspp::JsSymbol::to_std_string() const
9
+ {
10
+ return "Symbol(" + description + ")";
11
+ }
12
+
13
+ jspp::AnyValue jspp::JsSymbol::get_property(const std::string &key)
14
+ {
15
+ // check prototype
16
+ auto proto_it = SymbolPrototypes::get(key, this);
17
+ if (proto_it.has_value())
18
+ {
19
+ return AnyValue::resolve_property_for_read(proto_it.value());
20
+ }
21
+ // not found
22
+ return AnyValue::make_undefined();
23
+ }
@@ -0,0 +1,88 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include <coroutine>
5
+ #include <optional>
6
+ #include <iostream>
7
+ #include <utility>
8
+ #include <exception>
9
+
10
+ namespace jspp
11
+ {
12
+ // Forward declaration of AnyValue
13
+ class AnyValue;
14
+
15
+ template <typename T>
16
+ class JsIterator
17
+ {
18
+ public:
19
+ struct NextResult
20
+ {
21
+ std::optional<T> value;
22
+ bool done;
23
+ };
24
+ struct promise_type
25
+ {
26
+ std::optional<T> current_value;
27
+ std::exception_ptr exception_;
28
+
29
+ JsIterator get_return_object()
30
+ {
31
+ return JsIterator{
32
+ std::coroutine_handle<promise_type>::from_promise(*this)};
33
+ }
34
+
35
+ std::suspend_always initial_suspend() noexcept { return {}; }
36
+
37
+ // valid js generators allow access to the return value after completion,
38
+ // so we must suspend at the end to keep the promise (and value) alive.
39
+ std::suspend_always final_suspend() noexcept { return {}; }
40
+
41
+ // Handle co_yield
42
+ template <typename From>
43
+ std::suspend_always yield_value(From &&from)
44
+ {
45
+ current_value = std::forward<From>(from);
46
+ return {};
47
+ }
48
+
49
+ // Handle co_return
50
+ // This replaces return_void.
51
+ // It captures the final value and moves to final_suspend (implicit).
52
+ template <typename From>
53
+ void return_value(From &&from)
54
+ {
55
+ current_value = std::forward<From>(from);
56
+ }
57
+
58
+ void unhandled_exception()
59
+ {
60
+ exception_ = std::current_exception();
61
+ }
62
+ };
63
+
64
+ using handle_type = std::coroutine_handle<promise_type>;
65
+ handle_type handle;
66
+
67
+ explicit JsIterator(handle_type h) : handle(h) {}
68
+ JsIterator(JsIterator &&other) noexcept : handle(std::exchange(other.handle, nullptr)) {}
69
+
70
+ // Delete copy constructor/assignment to ensure unique ownership of the handle
71
+ JsIterator(const JsIterator &) = delete;
72
+ JsIterator &operator=(const JsIterator &) = delete;
73
+
74
+ ~JsIterator()
75
+ {
76
+ if (handle)
77
+ handle.destroy();
78
+ }
79
+
80
+ std::unordered_map<std::string, AnyValue> props;
81
+
82
+ std::string to_std_string() const;
83
+ NextResult next();
84
+ std::vector<std::optional<T>> to_vector();
85
+ AnyValue get_property(const std::string &key);
86
+ AnyValue set_property(const std::string &key, const AnyValue &value);
87
+ };
88
+ }
@@ -4,11 +4,12 @@
4
4
 
5
5
  namespace jspp
6
6
  {
7
+ // Forward declaration of AnyValue
7
8
  class AnyValue;
8
9
 
9
10
  struct JsObject
10
11
  {
11
- std::unordered_map<std::string, AnyValue> props;
12
+ std::map<std::string, AnyValue> props;
12
13
 
13
14
  std::string to_std_string() const;
14
15
  AnyValue get_property(const std::string &key);
@@ -14,24 +14,31 @@ namespace jspp
14
14
  {
15
15
  inline std::optional<AnyValue> get(const std::string &key, JsArray *self)
16
16
  {
17
-
18
17
  // --- toString() method ---
19
- if (key == "toString")
18
+ if (key == "toString" || key == WellKnownSymbols::toString->key)
20
19
  {
21
- return AnyValue::make_function([&self](const std::vector<AnyValue> &args) -> AnyValue
20
+ return AnyValue::make_function([self](const std::vector<AnyValue> &_) -> AnyValue
22
21
  { return AnyValue::make_string(self->to_std_string()); },
23
22
  key);
24
23
  }
25
24
 
25
+ // --- [Symbol.iterator]() method ---
26
+ if (key == WellKnownSymbols::iterator->key)
27
+ {
28
+ return AnyValue::make_generator([self](const std::vector<AnyValue> &_) -> AnyValue
29
+ { return AnyValue::from_iterator(self->get_iterator()); },
30
+ key);
31
+ }
32
+
26
33
  // --- length property ---
27
34
  if (key == "length")
28
35
  {
29
- auto getter = [&self](const std::vector<AnyValue> &args) -> AnyValue
36
+ auto getter = [self](const std::vector<AnyValue> &args) -> AnyValue
30
37
  {
31
38
  return AnyValue::make_number(self->length);
32
39
  };
33
40
 
34
- auto setter = [&self](const std::vector<AnyValue> &args) -> AnyValue
41
+ auto setter = [self](const std::vector<AnyValue> &args) -> AnyValue
35
42
  {
36
43
  if (args.empty())
37
44
  {
@@ -79,7 +86,7 @@ namespace jspp
79
86
  // --- push() method ---
80
87
  if (key == "push")
81
88
  {
82
- return AnyValue::make_function([&self](const std::vector<AnyValue> &args) -> AnyValue
89
+ return AnyValue::make_function([self](const std::vector<AnyValue> &args) -> AnyValue
83
90
  {
84
91
  for (const auto &arg : args)
85
92
  {
@@ -92,7 +99,7 @@ namespace jspp
92
99
  // --- pop() method ---
93
100
  if (key == "pop")
94
101
  {
95
- return AnyValue::make_function([&self](const std::vector<AnyValue> &args) -> AnyValue
102
+ return AnyValue::make_function([self](const std::vector<AnyValue> &args) -> AnyValue
96
103
  {
97
104
  if (self->length == 0)
98
105
  {
@@ -117,7 +124,7 @@ namespace jspp
117
124
  // --- shift() method ---
118
125
  if (key == "shift")
119
126
  {
120
- return AnyValue::make_function([&self](const std::vector<AnyValue> &args) -> AnyValue
127
+ return AnyValue::make_function([self](const std::vector<AnyValue> &args) -> AnyValue
121
128
  {
122
129
  if (self->length == 0)
123
130
  {
@@ -148,7 +155,7 @@ namespace jspp
148
155
  // --- unshift() method ---
149
156
  if (key == "unshift")
150
157
  {
151
- return AnyValue::make_function([&self](const std::vector<AnyValue> &args) -> AnyValue
158
+ return AnyValue::make_function([self](const std::vector<AnyValue> &args) -> AnyValue
152
159
  {
153
160
  size_t args_count = args.size();
154
161
  if (args_count == 0)
@@ -175,7 +182,7 @@ namespace jspp
175
182
  // --- join() method ---
176
183
  if (key == "join")
177
184
  {
178
- return AnyValue::make_function([&self](const std::vector<AnyValue> &args) -> AnyValue
185
+ return AnyValue::make_function([self](const std::vector<AnyValue> &args) -> AnyValue
179
186
  {
180
187
  std::string sep = ",";
181
188
  if (!args.empty() && !args[0].is_undefined())
@@ -203,7 +210,7 @@ namespace jspp
203
210
  // --- forEach() method ---
204
211
  if (key == "forEach")
205
212
  {
206
- return AnyValue::make_function([&self](const std::vector<AnyValue> &args) -> AnyValue
213
+ return AnyValue::make_function([self](const std::vector<AnyValue> &args) -> AnyValue
207
214
  {
208
215
  if (args.empty() || !args[0].is_function())
209
216
  {
@@ -0,0 +1,26 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "values/function.hpp"
5
+ #include "any_value.hpp"
6
+ #include "error.hpp"
7
+ #include "operators.hpp"
8
+
9
+ namespace jspp
10
+ {
11
+ namespace FunctionPrototypes
12
+ {
13
+ inline std::optional<AnyValue> get(const std::string &key, JsFunction *self)
14
+ {
15
+ // --- toString() method ---
16
+ if (key == "toString" || key == WellKnownSymbols::toString->key)
17
+ {
18
+ return AnyValue::make_function([self](const std::vector<AnyValue> &_) -> AnyValue
19
+ { return AnyValue::make_string(self->to_std_string()); },
20
+ key);
21
+ }
22
+
23
+ return std::nullopt;
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,57 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "values/iterator.hpp"
5
+ #include "any_value.hpp"
6
+ #include "error.hpp"
7
+ #include "operators.hpp"
8
+
9
+ namespace jspp
10
+ {
11
+ namespace IteratorPrototypes
12
+ {
13
+ inline std::optional<AnyValue> get(const std::string &key, JsIterator<AnyValue> *self)
14
+ {
15
+ // --- toString() method ---
16
+ if (key == "toString" || key == WellKnownSymbols::toString->key)
17
+ {
18
+ return AnyValue::make_function([self](const std::vector<AnyValue> &) -> AnyValue
19
+ { return AnyValue::make_string(self->to_std_string()); },
20
+ key);
21
+ }
22
+ // --- [Symbol.iterator]() method ---
23
+ if (key == WellKnownSymbols::iterator->key)
24
+ {
25
+ return AnyValue::make_function([self](const std::vector<AnyValue> &) -> AnyValue
26
+ {
27
+ // An iterator's iterator is itself.
28
+ // We need to return an AnyValue that holds a shared_ptr to this JsIterator.
29
+ // Since we only have a raw pointer `self`, we can't directly make a new shared_ptr.
30
+ // We'll return an AnyValue wrapping the raw pointer for now.
31
+ // This relies on the calling context to manage lifetime, which is true for `for-of`.
32
+ // A better solution might involve passing a shared_ptr to `self` into the prototype getters.
33
+ // For now, let's assume the object containing the iterator is alive.
34
+ return AnyValue::from_iterator_ref(self); },
35
+ key);
36
+ }
37
+ // --- next() method ---
38
+ if (key == "next")
39
+ {
40
+ return AnyValue::make_function([self](const std::vector<AnyValue> &) -> AnyValue
41
+ {
42
+ auto res = self->next();
43
+ return AnyValue::make_object({{"value",res.value.value_or(AnyValue::make_undefined())},{"done",AnyValue::make_boolean(res.done)},}); },
44
+ key);
45
+ }
46
+ // --- toArray() method ---
47
+ if (key == "toArray")
48
+ {
49
+ return AnyValue::make_function([self](const std::vector<AnyValue> &) -> AnyValue
50
+ { return AnyValue::make_array(self->to_vector()); },
51
+ key);
52
+ }
53
+
54
+ return std::nullopt;
55
+ }
56
+ }
57
+ }