@ugo-studio/jspp 0.1.1 → 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 +7 -5
  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 +2 -2
  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
@@ -0,0 +1,225 @@
1
+ #pragma once
2
+
3
+ #include "types.hpp"
4
+ #include "any_value.hpp"
5
+ #include "values/string.hpp"
6
+
7
+ const bool jspp::AnyValue::is_truthy() const noexcept
8
+ {
9
+ switch (storage.type)
10
+ {
11
+ case JsType::Boolean:
12
+ return storage.boolean;
13
+ case JsType::Number:
14
+ return storage.number != 0.0;
15
+ case JsType::String:
16
+ return !storage.str->value.empty();
17
+ case JsType::Undefined:
18
+ return false;
19
+ case JsType::Null:
20
+ return false;
21
+ case JsType::Uninitialized:
22
+ return false;
23
+ default:
24
+ return true;
25
+ }
26
+ }
27
+
28
+ const bool jspp::AnyValue::is_strictly_equal_to(const AnyValue &other) const noexcept
29
+ {
30
+ if (storage.type == other.storage.type)
31
+ {
32
+ switch (storage.type)
33
+ {
34
+ case JsType::Boolean:
35
+ return storage.boolean == other.storage.boolean;
36
+ case JsType::Number:
37
+ return storage.number == other.storage.number;
38
+ case JsType::String:
39
+ return (storage.str->value == other.storage.str->value);
40
+ case JsType::Array:
41
+ return (storage.array == other.storage.array);
42
+ case JsType::Object:
43
+ return (storage.object == other.storage.object);
44
+ case JsType::Function:
45
+ return (storage.function == other.storage.function);
46
+ case JsType::Symbol:
47
+ // Symbols are unique by reference/pointer identity
48
+ return (storage.symbol == other.storage.symbol);
49
+ case JsType::DataDescriptor:
50
+ return (resolve_property_for_read(*this).is_strictly_equal_to(resolve_property_for_read(other)));
51
+ case JsType::AccessorDescriptor:
52
+ return (resolve_property_for_read(*this).is_strictly_equal_to(resolve_property_for_read(other)));
53
+ default:
54
+ return true;
55
+ }
56
+ }
57
+ return false;
58
+ }
59
+ const bool jspp::AnyValue::is_equal_to(const AnyValue &other) const noexcept
60
+ {
61
+ // Implements JavaScript's Abstract Equality Comparison Algorithm (==)
62
+ // Step 1: If types are the same, use strict equality (===)
63
+ if (storage.type == other.storage.type)
64
+ {
65
+ return is_strictly_equal_to(other);
66
+ }
67
+ // Steps 2 & 3: null == undefined
68
+ if ((is_null() && other.is_undefined()) || (is_undefined() && other.is_null()))
69
+ {
70
+ return true;
71
+ }
72
+ // Step 4 & 5: number == string
73
+ if (is_number() && other.is_string())
74
+ {
75
+ double num_this = this->as_double();
76
+ double num_other;
77
+ try
78
+ {
79
+ const std::string &s = other.as_string()->value;
80
+ // JS considers empty string or whitespace-only string to be 0
81
+ if (s.empty() || std::all_of(s.begin(), s.end(), [](unsigned char c)
82
+ { return std::isspace(c); }))
83
+ {
84
+ num_other = 0.0;
85
+ }
86
+ else
87
+ {
88
+ size_t pos;
89
+ num_other = std::stod(s, &pos);
90
+ // Check if the entire string was consumed, allowing for trailing whitespace
91
+ while (pos < s.length() && std::isspace(static_cast<unsigned char>(s[pos])))
92
+ {
93
+ pos++;
94
+ }
95
+ if (pos != s.length())
96
+ {
97
+ num_other = std::numeric_limits<double>::quiet_NaN();
98
+ }
99
+ }
100
+ }
101
+ catch (...)
102
+ {
103
+ num_other = std::numeric_limits<double>::quiet_NaN();
104
+ }
105
+ return num_this == num_other;
106
+ }
107
+ if (is_string() && other.is_number())
108
+ {
109
+ // Delegate to the other operand to avoid code duplication
110
+ return other.is_equal_to(*this);
111
+ }
112
+ // Step 6 & 7: boolean == any
113
+ if (is_boolean())
114
+ {
115
+ // Convert boolean to number and re-compare
116
+ return AnyValue::make_number(as_boolean() ? 1.0 : 0.0).is_equal_to(other);
117
+ }
118
+ if (other.is_boolean())
119
+ {
120
+ // Convert boolean to number and re-compare
121
+ return is_equal_to(AnyValue::make_number(other.as_boolean() ? 1.0 : 0.0));
122
+ }
123
+ // Step 8 & 9: object == (string or number or symbol)
124
+ // Simplified: Objects convert to primitives.
125
+ if ((is_object() || is_array() || is_function()) && (other.is_string() || other.is_number() || other.is_symbol()))
126
+ {
127
+ // Convert object to primitive (string) and re-compare.
128
+ // This is a simplification of JS's ToPrimitive.
129
+ return AnyValue::make_string(to_std_string()).is_equal_to(other);
130
+ }
131
+ if ((other.is_object() || other.is_array() || other.is_function()) && (is_string() || is_number() || is_symbol()))
132
+ {
133
+ return other.is_equal_to(*this);
134
+ }
135
+ // Step 10: Parse datacriptor or accessor descriptor to primitive and re-compare
136
+ if (is_data_descriptor() || is_accessor_descriptor())
137
+ {
138
+ AnyValue prim = resolve_property_for_read(*this);
139
+ return prim.is_equal_to(other);
140
+ }
141
+ // Step 11: All other cases (e.g., object == null) are false.
142
+ return false;
143
+ }
144
+
145
+ const jspp::AnyValue jspp::AnyValue::is_strictly_equal_to_primitive(const AnyValue &other) const noexcept
146
+ {
147
+ return AnyValue::make_boolean(is_strictly_equal_to(other));
148
+ }
149
+ const jspp::AnyValue jspp::AnyValue::is_equal_to_primitive(const AnyValue &other) const noexcept
150
+ {
151
+ return AnyValue::make_boolean(is_equal_to(other));
152
+ }
153
+
154
+ const jspp::AnyValue jspp::AnyValue::not_strictly_equal_to_primitive(const AnyValue &other) const noexcept
155
+ {
156
+ return AnyValue::make_boolean(!is_strictly_equal_to(other));
157
+ }
158
+ const jspp::AnyValue jspp::AnyValue::not_equal_to_primitive(const AnyValue &other) const noexcept
159
+ {
160
+ return AnyValue::make_boolean(!is_equal_to(other));
161
+ }
162
+
163
+ const std::string jspp::AnyValue::to_std_string() const noexcept
164
+ {
165
+ switch (storage.type)
166
+ {
167
+ case JsType::Undefined:
168
+ return "undefined";
169
+ case JsType::Null:
170
+ return "null";
171
+ case JsType::Boolean:
172
+ return storage.boolean ? "true" : "false";
173
+ case JsType::String:
174
+ return storage.str->to_std_string();
175
+ case JsType::Object:
176
+ return storage.object->to_std_string();
177
+ case JsType::Array:
178
+ return storage.array->to_std_string();
179
+ case JsType::Function:
180
+ return storage.function->to_std_string();
181
+ case JsType::Iterator:
182
+ return storage.iterator->to_std_string();
183
+ case JsType::Symbol:
184
+ return storage.symbol->to_std_string();
185
+ case JsType::DataDescriptor:
186
+ return storage.data_desc->value->to_std_string();
187
+ case JsType::AccessorDescriptor:
188
+ {
189
+ if (storage.accessor_desc->get.has_value())
190
+ return storage.accessor_desc->get.value()({}).to_std_string();
191
+ else
192
+ return "undefined";
193
+ }
194
+ case JsType::Number:
195
+ {
196
+ if (std::isnan(storage.number))
197
+ {
198
+ return "NaN";
199
+ }
200
+ if (std::abs(storage.number) >= 1e21 || (std::abs(storage.number) > 0 && std::abs(storage.number) < 1e-6))
201
+ {
202
+ std::ostringstream oss;
203
+ oss << std::scientific << std::setprecision(4) << storage.number;
204
+ return oss.str();
205
+ }
206
+ else
207
+ {
208
+ std::ostringstream oss;
209
+ oss << std::setprecision(6) << std::fixed << storage.number;
210
+ std::string s = oss.str();
211
+ s.erase(s.find_last_not_of('0') + 1, std::string::npos);
212
+ if (!s.empty() && s.back() == '.')
213
+ {
214
+ s.pop_back();
215
+ }
216
+ return s;
217
+ }
218
+ }
219
+ // Uninitialized and default should not be reached under normal circumstances
220
+ case JsType::Uninitialized:
221
+ return "<uninitialized>";
222
+ default:
223
+ return "";
224
+ }
225
+ }
@@ -12,8 +12,8 @@ const char *jspp::RuntimeError::what() const noexcept
12
12
  jspp::RuntimeError jspp::RuntimeError::make_error(const std::string &message, const std::string &name = "Error")
13
13
  {
14
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
- {
15
+ (*errorObj).set_own_property(WellKnownSymbols::toString->key, AnyValue::make_function([errorObj](const std::vector<AnyValue> &) -> AnyValue
16
+ {
17
17
  AnyValue name = (*errorObj).get_own_property("name");
18
18
  AnyValue message = (*errorObj).get_own_property("message");
19
19
  std::string str = "";
@@ -25,7 +25,7 @@ jspp::RuntimeError jspp::RuntimeError::make_error(const std::string &message, co
25
25
  if (message.is_string())
26
26
  str += message.to_std_string();
27
27
  return AnyValue::make_string(str); },
28
- WellKnownSymbols::toString));
28
+ WellKnownSymbols::toString->key));
29
29
  return RuntimeError(errorObj);
30
30
  }
31
31
  jspp::AnyValue jspp::RuntimeError::error_to_value(const std::exception &ex)
@@ -4,21 +4,33 @@
4
4
  #include "well_known_symbols.hpp"
5
5
 
6
6
  // values
7
+ #include "values/symbol.hpp"
7
8
  #include "values/non_values.hpp"
8
9
  #include "values/object.hpp"
9
10
  #include "values/array.hpp"
10
11
  #include "values/function.hpp"
12
+ #include "values/iterator.hpp"
13
+ #include "values/string.hpp"
14
+
11
15
  #include "error.hpp"
12
16
  #include "descriptors.hpp"
13
17
  #include "any_value.hpp"
18
+ #include "any_value_helpers.hpp"
14
19
  #include "error_helpers.hpp"
20
+
21
+ #include "values/prototypes/symbol.hpp"
15
22
  #include "values/prototypes/string.hpp"
16
23
  #include "values/prototypes/object.hpp"
17
24
  #include "values/prototypes/array.hpp"
18
25
  #include "values/prototypes/function.hpp"
19
- #include "values/operators/object.hpp"
20
- #include "values/operators/array.hpp"
21
- #include "values/operators/function.hpp"
26
+ #include "values/prototypes/iterator.hpp"
27
+
28
+ #include "values/helpers/symbol.hpp"
29
+ #include "values/helpers/object.hpp"
30
+ #include "values/helpers/array.hpp"
31
+ #include "values/helpers/function.hpp"
32
+ #include "values/helpers/iterator.hpp"
33
+ #include "values/helpers/string.hpp"
22
34
 
23
35
  // helpers
24
36
  #include "operators.hpp"
@@ -26,4 +38,6 @@
26
38
  #include "log_string.hpp"
27
39
 
28
40
  // js standard libraries
41
+ #include "library/symbol.hpp"
29
42
  #include "library/console.hpp"
43
+ #include "library/performance.hpp"
@@ -1,6 +1,6 @@
1
1
  #pragma once
2
2
 
3
- #include "chrono"
3
+ #include <chrono>
4
4
  #include "types.hpp"
5
5
  #include "values/non_values.hpp"
6
6
  #include "values/object.hpp"
@@ -12,7 +12,7 @@
12
12
  #include <sstream>
13
13
  #include <iomanip>
14
14
 
15
- static std::unordered_map<std::string, std::chrono::steady_clock::time_point> timers = {};
15
+ static std::map<std::string, std::chrono::steady_clock::time_point> timers = {};
16
16
 
17
17
  auto logFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue> &args)
18
18
  {
@@ -23,7 +23,7 @@ auto logFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue>
23
23
  std::cout << " ";
24
24
  }
25
25
  std::cout << "\n";
26
- return jspp::AnyValue::make_undefined(); }, "");
26
+ return jspp::AnyValue::make_undefined(); }, "log");
27
27
  auto warnFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue> &args)
28
28
  {
29
29
  std::cerr << "\033[33m";
@@ -34,7 +34,7 @@ auto warnFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue>
34
34
  std::cout << " ";
35
35
  }
36
36
  std::cerr << "\033[0m" << "\n"; // reset
37
- return jspp::AnyValue::make_undefined(); }, "");
37
+ return jspp::AnyValue::make_undefined(); }, "warn");
38
38
  auto errorFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue> &args)
39
39
  {
40
40
  std::cerr << "\033[31m";
@@ -45,13 +45,13 @@ auto errorFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue
45
45
  std::cout << " ";
46
46
  }
47
47
  std::cerr << "\033[0m" << "\n"; // reset
48
- return jspp::AnyValue::make_undefined(); }, "");
48
+ return jspp::AnyValue::make_undefined(); }, "error");
49
49
  auto timeFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue> &args)
50
50
  {
51
51
  auto start = std::chrono::steady_clock::now(); // capture immediately
52
52
  auto key_str = args.size() > 0 ? args[0].to_std_string() : "default";
53
53
  timers[key_str] = start;
54
- return jspp::AnyValue::make_undefined(); }, "");
54
+ return jspp::AnyValue::make_undefined(); }, "time");
55
55
 
56
56
  // helper to format duration in ms -> ms/s/m/h with nice precision
57
57
  static auto format_duration = [](double ms) -> std::string
@@ -100,7 +100,7 @@ auto timeEndFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyVal
100
100
  {
101
101
  std::cout << "Timer '" << key_str << "' does not exist." << "\n";
102
102
  }
103
- return jspp::AnyValue::make_undefined(); }, "");
103
+ return jspp::AnyValue::make_undefined(); }, "timeEnd");
104
104
 
105
105
  inline auto console = jspp::AnyValue::make_object({
106
106
  {"log", logFn},
@@ -0,0 +1,25 @@
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
+
10
+ inline auto performance = jspp::AnyValue::make_object({
11
+ {"now",
12
+ jspp::AnyValue::make_function(
13
+ // [C++14 Feature] Generalized Lambda Capture
14
+ // We initialize 'startTime' RIGHT HERE inside the [].
15
+ // It acts like a private variable stored inside this specific function.
16
+ [startTime = std::chrono::steady_clock::now()](const std::vector<jspp::AnyValue> &)
17
+ {
18
+ // We calculate the diff against the captured startTime
19
+ std::chrono::duration<double, std::milli> duration =
20
+ std::chrono::steady_clock::now() - startTime;
21
+
22
+ return jspp::AnyValue::make_number(duration.count());
23
+ },
24
+ "now")},
25
+ });
@@ -1,8 +1,65 @@
1
1
  #pragma once
2
2
 
3
3
  #include "types.hpp"
4
- #include "object.hpp"
5
4
  #include "well_known_symbols.hpp"
6
5
 
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)}});
6
+ #include "values/object.hpp"
7
+ #include "any_value.hpp"
8
+
9
+ inline auto Symbol = jspp::AnyValue::make_object({
10
+ {"iterator", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::iterator)},
11
+ {"toString", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::toString)},
12
+ });
13
+
14
+ // #pragma once
15
+
16
+ // #include "types.hpp"
17
+ // #include "well_known_symbols.hpp"
18
+ // #include "values/object.hpp"
19
+ // #include "any_value.hpp"
20
+
21
+ // // We define the Symbol constructor function
22
+ // inline auto symbolConstructor = [](const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
23
+ // std::string desc = "";
24
+ // if (!args.empty() && !args[0].is_undefined()) {
25
+ // desc = args[0].to_std_string();
26
+ // }
27
+ // return jspp::AnyValue::make_symbol(desc);
28
+ // };
29
+
30
+ // inline auto Symbol = jspp::AnyValue::make_function(symbolConstructor, "Symbol");
31
+
32
+ // // Implementation of Symbol.for(key)
33
+ // inline auto forFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
34
+ // std::string key = "";
35
+ // if (!args.empty()) {
36
+ // key = args[0].to_std_string();
37
+ // }
38
+ // auto symPtr = jspp::JsSymbol::for_global(key);
39
+ // return jspp::AnyValue::from_symbol(symPtr);
40
+ // }, "for");
41
+
42
+ // // Implementation of Symbol.keyFor(sym)
43
+ // inline auto keyForFn = jspp::AnyValue::make_function([](const std::vector<jspp::AnyValue>& args) -> jspp::AnyValue {
44
+ // if (args.empty() || !args[0].is_symbol()) {
45
+ // throw jspp::RuntimeError::make_error("Symbol.keyFor requires a symbol argument", "TypeError");
46
+ // }
47
+
48
+ // jspp::JsSymbol* sym = args[0].as_symbol();
49
+ // auto result = jspp::JsSymbol::key_for(sym);
50
+
51
+ // if (result.has_value()) {
52
+ // return jspp::AnyValue::make_string(result.value());
53
+ // } else {
54
+ // return jspp::AnyValue::make_undefined();
55
+ // }
56
+ // }, "keyFor");
57
+
58
+ // // Attach static properties/methods to the Symbol object
59
+ // struct SymbolInit {
60
+ // SymbolInit() {
61
+ // Symbol.set_own_property("iterator", jspp::AnyValue::from_symbol(jspp::WellKnownSymbols::iterator));
62
+ // Symbol.set_own_property("for", forFn);
63
+ // Symbol.set_own_property("keyFor", keyForFn);
64
+ // }
65
+ // } symbolInit;
@@ -26,6 +26,7 @@ namespace jspp
26
26
  const std::string RESET = "\033[0m";
27
27
  const std::string GREEN = "\033[32m";
28
28
  const std::string YELLOW = "\033[33m";
29
+ const std::string BLUE = "\033[94m";
29
30
  const std::string CYAN = "\033[36m";
30
31
  const std::string MAGENTA = "\033[35m";
31
32
  const std::string BRIGHT_BLACK = "\033[90m"; // Grey
@@ -90,9 +91,11 @@ namespace jspp
90
91
  return Color::YELLOW + std::string(val.as_boolean() ? "true" : "false") + Color::RESET;
91
92
  if (val.is_number())
92
93
  return Color::YELLOW + val.to_std_string() + Color::RESET;
94
+ if (val.is_symbol())
95
+ return Color::BLUE + val.to_std_string() + Color::RESET;
93
96
  if (val.is_string())
94
97
  {
95
- const std::string &s = *val.as_string();
98
+ const std::string &s = val.as_string()->value;
96
99
  if (depth == 0)
97
100
  return truncate_string(s);
98
101
  return Color::GREEN + std::string("\"") + truncate_string(s) + "\"" + Color::RESET;
@@ -100,8 +103,9 @@ namespace jspp
100
103
  if (val.is_function())
101
104
  {
102
105
  auto fn = val.as_function();
106
+ auto type_part = fn->is_generator ? "GeneratorFunction" : "Function";
103
107
  auto name_part = fn->name.size() > 0 ? ": " + fn->name : "";
104
- return Color::CYAN + std::string("[Function") + name_part + "]" + Color::RESET;
108
+ return Color::CYAN + "[" + type_part + name_part + "]" + Color::RESET;
105
109
  }
106
110
 
107
111
  // Depth limit
@@ -137,12 +141,12 @@ namespace jspp
137
141
  auto obj = val.as_object();
138
142
 
139
143
  // If custom toString exists on the object, prefer it
140
- auto itToString = obj->props.find(jspp::WellKnownSymbols::toString);
144
+ auto itToString = obj->props.find(jspp::WellKnownSymbols::toString->key);
141
145
  if (itToString != obj->props.end() && itToString->second.is_function())
142
146
  {
143
147
  try
144
148
  {
145
- auto result = itToString->second.as_function("toString")->call({});
149
+ auto result = itToString->second.as_function()->call({});
146
150
  return to_log_string(result, visited, depth);
147
151
  }
148
152
  catch (...)
@@ -230,12 +234,12 @@ namespace jspp
230
234
  size_t item_count = static_cast<size_t>(arr->length);
231
235
 
232
236
  // If custom toString exists on the object, prefer it
233
- auto itToString = arr->props.find(jspp::WellKnownSymbols::toString);
237
+ auto itToString = arr->props.find(jspp::WellKnownSymbols::toString->key);
234
238
  if (depth > 0 && itToString != arr->props.end() && itToString->second.is_function())
235
239
  {
236
240
  try
237
241
  {
238
- auto result = itToString->second.as_function("toString")->call({});
242
+ auto result = itToString->second.as_function()->call({});
239
243
  return to_log_string(result, visited, depth);
240
244
  }
241
245
  catch (...)
@@ -23,7 +23,7 @@ namespace jspp
23
23
  return val.as_boolean() ? 1.0 : 0.0;
24
24
  if (val.is_string())
25
25
  {
26
- const std::string &s = *val.as_string();
26
+ const std::string &s = val.as_string()->value;
27
27
  // JS considers empty or whitespace-only strings as 0.
28
28
  if (s.empty() || std::all_of(s.begin(), s.end(), isspace))
29
29
  return 0.0;
@@ -116,7 +116,7 @@ namespace jspp
116
116
  {
117
117
  // Simplified Abstract Relational Comparison
118
118
  if (lhs.is_string() && rhs.is_string())
119
- return AnyValue::make_boolean(*lhs.as_string() < *rhs.as_string());
119
+ return AnyValue::make_boolean(lhs.as_string()->value < rhs.as_string()->value);
120
120
 
121
121
  double l = Operators_Private::ToNumber(lhs);
122
122
  double r = Operators_Private::ToNumber(rhs);
@@ -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,51 @@
17
18
  // JSPP standard library
18
19
  namespace jspp
19
20
  {
20
- // Forward declarations
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 JsSymbol; // can set property (but usually doesn't have own props)
30
+
31
+ template <typename T>
32
+ class JsIterator; // can set property
27
33
 
28
34
  // Object property configuration forward declarations
29
35
  struct DataDescriptor;
30
36
  struct AccessorDescriptor;
31
37
 
32
- // Dynamic AnyValue
33
- class AnyValue;
34
-
35
38
  // Custom runtime exception
36
39
  struct RuntimeError;
37
40
 
41
+ // Dynamic AnyValue
42
+ class AnyValue;
43
+
38
44
  // Arithemetic operators
39
45
  inline AnyValue pow(const AnyValue &lhs, const AnyValue &rhs);
40
46
 
41
47
  // AnyValue prototypes
42
48
  namespace StringPrototypes
43
49
  {
44
- inline std::optional<AnyValue> get(const std::string &key, const std::unique_ptr<std::string> &self);
50
+ inline std::optional<AnyValue> get(const std::string &key, JsString *self);
45
51
  }
46
52
  namespace ArrayPrototypes
47
53
  {
48
54
  inline std::optional<AnyValue> get(const std::string &key, JsArray *self);
49
55
  }
50
- }
56
+ namespace FunctionPrototypes
57
+ {
58
+ inline std::optional<AnyValue> get(const std::string &key, JsFunction *self);
59
+ }
60
+ namespace IteratorPrototypes
61
+ {
62
+ inline std::optional<AnyValue> get(const std::string &key, JsIterator<AnyValue> *self);
63
+ }
64
+ namespace SymbolPrototypes
65
+ {
66
+ inline std::optional<AnyValue> get(const std::string &key, JsSymbol *self);
67
+ }
68
+ }
@@ -5,6 +5,7 @@
5
5
 
6
6
  namespace jspp
7
7
  {
8
+ // Forward declaration of AnyValue
8
9
  class AnyValue;
9
10
 
10
11
  struct JsArray
@@ -18,6 +19,7 @@ namespace jspp
18
19
  explicit JsArray(const std::vector<std::optional<AnyValue>> &items) : dense(items), length(items.size()) {}
19
20
 
20
21
  std::string to_std_string() const;
22
+ JsIterator<AnyValue> get_iterator();
21
23
 
22
24
  AnyValue get_property(const std::string &key);
23
25
  AnyValue get_property(uint32_t idx);
@@ -4,15 +4,47 @@
4
4
 
5
5
  namespace jspp
6
6
  {
7
+ // Forward declaration of AnyValue
7
8
  class AnyValue;
8
9
 
10
+ using JsFunctionCallable = std::variant<std::function<AnyValue(const std::vector<AnyValue> &)>, // 0: Normal
11
+ std::function<jspp::JsIterator<jspp::AnyValue>(const std::vector<jspp::AnyValue> &)>>; // 1: Generator
12
+
9
13
  struct JsFunction
10
14
  {
11
- std::function<AnyValue(const std::vector<AnyValue> &)> call;
15
+ JsFunctionCallable callable;
12
16
  std::string name;
13
17
  std::unordered_map<std::string, AnyValue> props;
18
+ bool is_generator;
19
+
20
+ // ---- Constructor A: infer is_generator using index() ----
21
+ JsFunction(const JsFunctionCallable &c,
22
+ std::string n = {},
23
+ std::unordered_map<std::string, AnyValue> p = {})
24
+ : callable(c),
25
+ name(std::move(n)),
26
+ props(std::move(p)),
27
+ is_generator(callable.index() == 1) // 1 = generator
28
+ {
29
+ }
30
+
31
+ // ---- Constructor B: explicitly set is_generator ----
32
+ JsFunction(const JsFunctionCallable &c,
33
+ bool is_gen,
34
+ std::string n = {},
35
+ std::unordered_map<std::string, AnyValue> p = {})
36
+ : callable(c),
37
+ name(std::move(n)),
38
+ props(std::move(p)),
39
+ is_generator(is_gen)
40
+ {
41
+ // Optional debug check (no RTTI, no variant visitation):
42
+ // if (callable.index() == 1 && !is_gen) { ... }
43
+ // if (callable.index() == 0 && is_gen) { ... }
44
+ }
14
45
 
15
46
  std::string to_std_string() const;
47
+ AnyValue call(const std::vector<AnyValue> &args);
16
48
  AnyValue get_property(const std::string &key);
17
49
  AnyValue set_property(const std::string &key, const AnyValue &value);
18
50
  };