@tishlang/tish-format 1.0.12 → 1.0.13

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 (164) hide show
  1. package/Cargo.toml +49 -0
  2. package/LICENSE +13 -0
  3. package/README.md +138 -0
  4. package/bin/tish-format +0 -0
  5. package/crates/js_to_tish/Cargo.toml +11 -0
  6. package/crates/js_to_tish/README.md +18 -0
  7. package/crates/js_to_tish/src/error.rs +55 -0
  8. package/crates/js_to_tish/src/lib.rs +11 -0
  9. package/crates/js_to_tish/src/span_util.rs +35 -0
  10. package/crates/js_to_tish/src/transform/expr.rs +610 -0
  11. package/crates/js_to_tish/src/transform/stmt.rs +503 -0
  12. package/crates/js_to_tish/src/transform.rs +60 -0
  13. package/crates/tish/Cargo.toml +54 -0
  14. package/crates/tish/src/cargo_native_registry.rs +32 -0
  15. package/crates/tish/src/cli_help.rs +565 -0
  16. package/crates/tish/src/main.rs +781 -0
  17. package/crates/tish/src/repl_completion.rs +200 -0
  18. package/crates/tish/tests/cargo_example_compile.rs +67 -0
  19. package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
  20. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
  21. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
  22. package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
  23. package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
  24. package/crates/tish/tests/integration_test.rs +1095 -0
  25. package/crates/tish/tests/run_optimize_stdout_parity.rs +50 -0
  26. package/crates/tish/tests/shortcircuit.rs +65 -0
  27. package/crates/tish_ast/Cargo.toml +9 -0
  28. package/crates/tish_ast/src/ast.rs +620 -0
  29. package/crates/tish_ast/src/lib.rs +5 -0
  30. package/crates/tish_build_utils/Cargo.toml +11 -0
  31. package/crates/tish_build_utils/src/lib.rs +577 -0
  32. package/crates/tish_builtins/Cargo.toml +20 -0
  33. package/crates/tish_builtins/src/array.rs +441 -0
  34. package/crates/tish_builtins/src/construct.rs +159 -0
  35. package/crates/tish_builtins/src/globals.rs +213 -0
  36. package/crates/tish_builtins/src/helpers.rs +35 -0
  37. package/crates/tish_builtins/src/lib.rs +16 -0
  38. package/crates/tish_builtins/src/math.rs +89 -0
  39. package/crates/tish_builtins/src/object.rs +36 -0
  40. package/crates/tish_builtins/src/string.rs +647 -0
  41. package/crates/tish_builtins/src/symbol.rs +83 -0
  42. package/crates/tish_bytecode/Cargo.toml +17 -0
  43. package/crates/tish_bytecode/src/chunk.rs +96 -0
  44. package/crates/tish_bytecode/src/compiler.rs +1760 -0
  45. package/crates/tish_bytecode/src/encoding.rs +100 -0
  46. package/crates/tish_bytecode/src/lib.rs +19 -0
  47. package/crates/tish_bytecode/src/opcode.rs +142 -0
  48. package/crates/tish_bytecode/src/peephole.rs +189 -0
  49. package/crates/tish_bytecode/src/serialize.rs +163 -0
  50. package/crates/tish_bytecode/tests/break_continue_bytecode.rs +44 -0
  51. package/crates/tish_bytecode/tests/constant_folding.rs +84 -0
  52. package/crates/tish_bytecode/tests/sort_optimization.rs +31 -0
  53. package/crates/tish_compile/Cargo.toml +26 -0
  54. package/crates/tish_compile/src/codegen.rs +5332 -0
  55. package/crates/tish_compile/src/infer.rs +292 -0
  56. package/crates/tish_compile/src/lib.rs +164 -0
  57. package/crates/tish_compile/src/resolve.rs +1388 -0
  58. package/crates/tish_compile/src/types.rs +501 -0
  59. package/crates/tish_compile_js/Cargo.toml +18 -0
  60. package/crates/tish_compile_js/examples/jsx_vdom_smoke.tish +8 -0
  61. package/crates/tish_compile_js/src/codegen.rs +871 -0
  62. package/crates/tish_compile_js/src/error.rs +20 -0
  63. package/crates/tish_compile_js/src/lib.rs +26 -0
  64. package/crates/tish_compile_js/src/tests_jsx.rs +350 -0
  65. package/crates/tish_compiler_wasm/Cargo.toml +21 -0
  66. package/crates/tish_compiler_wasm/src/lib.rs +57 -0
  67. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +473 -0
  68. package/crates/tish_core/Cargo.toml +26 -0
  69. package/crates/tish_core/src/console_style.rs +160 -0
  70. package/crates/tish_core/src/json.rs +387 -0
  71. package/crates/tish_core/src/lib.rs +17 -0
  72. package/crates/tish_core/src/macros.rs +36 -0
  73. package/crates/tish_core/src/uri.rs +118 -0
  74. package/crates/tish_core/src/value.rs +696 -0
  75. package/crates/tish_core/src/vmref.rs +178 -0
  76. package/crates/tish_cranelift/Cargo.toml +19 -0
  77. package/crates/tish_cranelift/src/lib.rs +43 -0
  78. package/crates/tish_cranelift/src/link.rs +117 -0
  79. package/crates/tish_cranelift/src/lower.rs +85 -0
  80. package/crates/tish_cranelift_runtime/Cargo.toml +25 -0
  81. package/crates/tish_cranelift_runtime/src/lib.rs +45 -0
  82. package/crates/tish_eval/Cargo.toml +45 -0
  83. package/crates/tish_eval/src/eval.rs +3717 -0
  84. package/crates/tish_eval/src/http.rs +188 -0
  85. package/crates/tish_eval/src/lib.rs +99 -0
  86. package/crates/tish_eval/src/natives.rs +399 -0
  87. package/crates/tish_eval/src/promise.rs +179 -0
  88. package/crates/tish_eval/src/regex.rs +299 -0
  89. package/crates/tish_eval/src/timers.rs +120 -0
  90. package/crates/tish_eval/src/value.rs +318 -0
  91. package/crates/tish_eval/src/value_convert.rs +111 -0
  92. package/crates/tish_fmt/Cargo.toml +16 -0
  93. package/crates/tish_fmt/src/bin/tish-fmt.rs +41 -0
  94. package/crates/tish_fmt/src/lib.rs +2101 -0
  95. package/crates/tish_jsx_web/Cargo.toml +9 -0
  96. package/crates/tish_jsx_web/README.md +5 -0
  97. package/crates/tish_jsx_web/src/lib.rs +2 -0
  98. package/crates/tish_lexer/Cargo.toml +9 -0
  99. package/crates/tish_lexer/src/lib.rs +716 -0
  100. package/crates/tish_lexer/src/token.rs +163 -0
  101. package/crates/tish_lint/Cargo.toml +18 -0
  102. package/crates/tish_lint/src/bin/tish-lint.rs +195 -0
  103. package/crates/tish_lint/src/lib.rs +289 -0
  104. package/crates/tish_llvm/Cargo.toml +13 -0
  105. package/crates/tish_llvm/src/lib.rs +115 -0
  106. package/crates/tish_lsp/Cargo.toml +25 -0
  107. package/crates/tish_lsp/README.md +26 -0
  108. package/crates/tish_lsp/src/builtin_goto.rs +362 -0
  109. package/crates/tish_lsp/src/import_goto.rs +562 -0
  110. package/crates/tish_lsp/src/main.rs +1046 -0
  111. package/crates/tish_native/Cargo.toml +16 -0
  112. package/crates/tish_native/src/build.rs +427 -0
  113. package/crates/tish_native/src/config.rs +48 -0
  114. package/crates/tish_native/src/lib.rs +416 -0
  115. package/crates/tish_opt/Cargo.toml +13 -0
  116. package/crates/tish_opt/src/lib.rs +943 -0
  117. package/crates/tish_parser/Cargo.toml +11 -0
  118. package/crates/tish_parser/src/lib.rs +332 -0
  119. package/crates/tish_parser/src/parser.rs +2304 -0
  120. package/crates/tish_pg/Cargo.toml +34 -0
  121. package/crates/tish_pg/README.md +38 -0
  122. package/crates/tish_pg/src/error.rs +52 -0
  123. package/crates/tish_pg/src/lib.rs +955 -0
  124. package/crates/tish_resolve/Cargo.toml +13 -0
  125. package/crates/tish_resolve/src/lib.rs +3561 -0
  126. package/crates/tish_resolve/src/pos.rs +141 -0
  127. package/crates/tish_runtime/Cargo.toml +96 -0
  128. package/crates/tish_runtime/src/http.rs +1298 -0
  129. package/crates/tish_runtime/src/http_fetch.rs +471 -0
  130. package/crates/tish_runtime/src/http_hyper.rs +418 -0
  131. package/crates/tish_runtime/src/http_prefork.rs +189 -0
  132. package/crates/tish_runtime/src/lib.rs +1192 -0
  133. package/crates/tish_runtime/src/native_promise.rs +15 -0
  134. package/crates/tish_runtime/src/promise.rs +248 -0
  135. package/crates/tish_runtime/src/promise_io.rs +38 -0
  136. package/crates/tish_runtime/src/timers.rs +166 -0
  137. package/crates/tish_runtime/src/ws.rs +761 -0
  138. package/crates/tish_runtime/tests/fetch_readable_stream.rs +102 -0
  139. package/crates/tish_ui/Cargo.toml +17 -0
  140. package/crates/tish_ui/src/jsx.rs +682 -0
  141. package/crates/tish_ui/src/lib.rs +20 -0
  142. package/crates/tish_ui/src/runtime/hooks.rs +569 -0
  143. package/crates/tish_ui/src/runtime/mod.rs +180 -0
  144. package/crates/tish_vm/Cargo.toml +47 -0
  145. package/crates/tish_vm/src/lib.rs +39 -0
  146. package/crates/tish_vm/src/vm.rs +2192 -0
  147. package/crates/tish_vm/tests/fixtures/or_string_cmd.tish +2 -0
  148. package/crates/tish_vm/tests/lexical_scope_declare.rs +34 -0
  149. package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +150 -0
  150. package/crates/tish_wasm/Cargo.toml +15 -0
  151. package/crates/tish_wasm/src/lib.rs +424 -0
  152. package/crates/tish_wasm_runtime/Cargo.toml +37 -0
  153. package/crates/tish_wasm_runtime/src/gpu.rs +413 -0
  154. package/crates/tish_wasm_runtime/src/lib.rs +42 -0
  155. package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
  156. package/crates/tishlang_cargo_bindgen/src/classify.rs +263 -0
  157. package/crates/tishlang_cargo_bindgen/src/discover.rs +125 -0
  158. package/crates/tishlang_cargo_bindgen/src/infer.rs +382 -0
  159. package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
  160. package/crates/tishlang_cargo_bindgen/src/main.rs +167 -0
  161. package/crates/tishlang_cargo_bindgen/src/metadata.rs +117 -0
  162. package/justfile +268 -0
  163. package/package.json +1 -1
  164. package/platform/darwin-arm64/tish-fmt +0 -0
@@ -0,0 +1,441 @@
1
+ //! Array builtin methods.
2
+
3
+ use crate::helpers::normalize_index;
4
+ use tishlang_core::Value;
5
+ use tishlang_core::VmRef;
6
+
7
+ /// Create a new array Value from a Vec of Values.
8
+ pub fn from_vec(v: Vec<Value>) -> Value {
9
+ Value::Array(VmRef::new(v))
10
+ }
11
+
12
+ /// Get the length of an array.
13
+ pub fn len(arr: &Value) -> Option<usize> {
14
+ match arr {
15
+ Value::Array(a) => Some(a.borrow().len()),
16
+ _ => None,
17
+ }
18
+ }
19
+
20
+ pub fn push(arr: &Value, args: &[Value]) -> Value {
21
+ if let Value::Array(arr) = arr {
22
+ let mut arr_mut = arr.borrow_mut();
23
+ for v in args {
24
+ arr_mut.push(v.clone());
25
+ }
26
+ Value::Number(arr_mut.len() as f64)
27
+ } else {
28
+ Value::Null
29
+ }
30
+ }
31
+
32
+ pub fn pop(arr: &Value) -> Value {
33
+ if let Value::Array(arr) = arr {
34
+ arr.borrow_mut().pop().unwrap_or(Value::Null)
35
+ } else {
36
+ Value::Null
37
+ }
38
+ }
39
+
40
+ pub fn shift(arr: &Value) -> Value {
41
+ if let Value::Array(arr) = arr {
42
+ let mut arr_mut = arr.borrow_mut();
43
+ if arr_mut.is_empty() {
44
+ Value::Null
45
+ } else {
46
+ arr_mut.remove(0)
47
+ }
48
+ } else {
49
+ Value::Null
50
+ }
51
+ }
52
+
53
+ pub fn unshift(arr: &Value, args: &[Value]) -> Value {
54
+ if let Value::Array(arr) = arr {
55
+ let mut arr_mut = arr.borrow_mut();
56
+ for (i, v) in args.iter().enumerate() {
57
+ arr_mut.insert(i, v.clone());
58
+ }
59
+ Value::Number(arr_mut.len() as f64)
60
+ } else {
61
+ Value::Null
62
+ }
63
+ }
64
+
65
+ pub fn index_of(arr: &Value, search: &Value) -> Value {
66
+ if let Value::Array(arr) = arr {
67
+ let arr_borrow = arr.borrow();
68
+ for (i, v) in arr_borrow.iter().enumerate() {
69
+ if v.strict_eq(search) {
70
+ return Value::Number(i as f64);
71
+ }
72
+ }
73
+ }
74
+ Value::Number(-1.0)
75
+ }
76
+
77
+ pub fn includes(arr: &Value, search: &Value, from: Option<&Value>) -> Value {
78
+ if let Value::Array(arr) = arr {
79
+ let arr_borrow = arr.borrow();
80
+ let len = arr_borrow.len() as i64;
81
+ let start = match from {
82
+ Some(Value::Number(n)) if *n >= 0.0 => (*n as i64).min(len).max(0) as usize,
83
+ Some(Value::Number(n)) if *n < 0.0 => ((len + *n as i64).max(0)) as usize,
84
+ _ => 0,
85
+ };
86
+ for v in arr_borrow.iter().skip(start) {
87
+ if v.strict_eq(search) {
88
+ return Value::Bool(true);
89
+ }
90
+ }
91
+ }
92
+ Value::Bool(false)
93
+ }
94
+
95
+ pub fn join(arr: &Value, sep: &Value) -> Value {
96
+ if let Value::Array(arr) = arr {
97
+ let separator = match sep {
98
+ Value::String(s) => s.to_string(),
99
+ _ => ",".to_string(),
100
+ };
101
+ let arr_borrow = arr.borrow();
102
+ let parts: Vec<String> = arr_borrow.iter().map(|v| v.to_display_string()).collect();
103
+ Value::String(parts.join(&separator).into())
104
+ } else {
105
+ Value::Null
106
+ }
107
+ }
108
+
109
+ pub fn reverse(arr: &Value) -> Value {
110
+ if let Value::Array(arr) = arr {
111
+ arr.borrow_mut().reverse();
112
+ Value::Array(arr.clone())
113
+ } else {
114
+ Value::Null
115
+ }
116
+ }
117
+
118
+ /// Fisher-Yates shuffle. Returns a new shuffled array (does not mutate).
119
+ pub fn shuffle(arr: &Value) -> Value {
120
+ if let Value::Array(arr) = arr {
121
+ let mut v = arr.borrow().clone();
122
+ use rand::seq::SliceRandom;
123
+ v.shuffle(&mut rand::rng());
124
+ Value::Array(VmRef::new(v))
125
+ } else {
126
+ Value::Null
127
+ }
128
+ }
129
+
130
+ pub fn splice(arr: &Value, start: &Value, delete_count: Option<&Value>, items: &[Value]) -> Value {
131
+ if let Value::Array(arr) = arr {
132
+ let mut arr_mut = arr.borrow_mut();
133
+ let len = arr_mut.len() as i64;
134
+ let start_idx = normalize_index(start, len, 0);
135
+ let del_count = match delete_count {
136
+ Some(Value::Number(n)) => (*n as i64).max(0) as usize,
137
+ _ => (len as usize).saturating_sub(start_idx),
138
+ };
139
+ let actual_delete = del_count.min(arr_mut.len().saturating_sub(start_idx));
140
+ let removed: Vec<Value> = arr_mut
141
+ .splice(start_idx..start_idx + actual_delete, items.iter().cloned())
142
+ .collect();
143
+ Value::Array(VmRef::new(removed))
144
+ } else {
145
+ Value::Null
146
+ }
147
+ }
148
+
149
+ pub fn slice(arr: &Value, start: &Value, end: &Value) -> Value {
150
+ if let Value::Array(arr) = arr {
151
+ let arr_borrow = arr.borrow();
152
+ let len = arr_borrow.len() as i64;
153
+ let start_idx = normalize_index(start, len, 0);
154
+ let end_idx = normalize_index(end, len, len as usize);
155
+ let sliced = if start_idx < end_idx {
156
+ arr_borrow[start_idx..end_idx].to_vec()
157
+ } else {
158
+ vec![]
159
+ };
160
+ Value::Array(VmRef::new(sliced))
161
+ } else {
162
+ Value::Null
163
+ }
164
+ }
165
+
166
+ pub fn concat(arr: &Value, args: &[Value]) -> Value {
167
+ if let Value::Array(arr) = arr {
168
+ let mut result = arr.borrow().clone();
169
+ for v in args {
170
+ if let Value::Array(other) = v {
171
+ result.extend(other.borrow().iter().cloned());
172
+ } else {
173
+ result.push(v.clone());
174
+ }
175
+ }
176
+ Value::Array(VmRef::new(result))
177
+ } else {
178
+ Value::Null
179
+ }
180
+ }
181
+
182
+ pub fn flat(arr: &Value, depth: &Value) -> Value {
183
+ fn flatten(arr: &[Value], depth: i32, result: &mut Vec<Value>) {
184
+ for v in arr {
185
+ if depth > 0 {
186
+ if let Value::Array(inner) = v {
187
+ flatten(&inner.borrow(), depth - 1, result);
188
+ continue;
189
+ }
190
+ }
191
+ result.push(v.clone());
192
+ }
193
+ }
194
+
195
+ if let Value::Array(arr) = arr {
196
+ let d = match depth {
197
+ Value::Number(n) => *n as i32,
198
+ _ => 1,
199
+ };
200
+ let mut result = Vec::new();
201
+ flatten(&arr.borrow(), d, &mut result);
202
+ Value::Array(VmRef::new(result))
203
+ } else {
204
+ Value::Null
205
+ }
206
+ }
207
+
208
+ // Higher-order array methods require a callback function.
209
+ // These take NativeFn from tishlang_core::Value::Function
210
+
211
+ pub fn map(arr: &Value, callback: &Value) -> Value {
212
+ if let (Value::Array(arr), Value::Function(cb)) = (arr, callback) {
213
+ let arr_borrow = arr.borrow();
214
+ let result: Vec<Value> = arr_borrow
215
+ .iter()
216
+ .enumerate()
217
+ .map(|(i, v)| cb(&[v.clone(), Value::Number(i as f64)]))
218
+ .collect();
219
+ Value::Array(VmRef::new(result))
220
+ } else {
221
+ Value::Null
222
+ }
223
+ }
224
+
225
+ pub fn filter(arr: &Value, callback: &Value) -> Value {
226
+ if let (Value::Array(arr), Value::Function(cb)) = (arr, callback) {
227
+ let arr_borrow = arr.borrow();
228
+ let result: Vec<Value> = arr_borrow
229
+ .iter()
230
+ .enumerate()
231
+ .filter_map(|(i, v)| {
232
+ let keep = cb(&[v.clone(), Value::Number(i as f64)]);
233
+ if keep.is_truthy() {
234
+ Some(v.clone())
235
+ } else {
236
+ None
237
+ }
238
+ })
239
+ .collect();
240
+ Value::Array(VmRef::new(result))
241
+ } else {
242
+ Value::Null
243
+ }
244
+ }
245
+
246
+ pub fn reduce(arr: &Value, callback: &Value, initial: &Value) -> Value {
247
+ if let (Value::Array(arr), Value::Function(cb)) = (arr, callback) {
248
+ let arr_borrow = arr.borrow();
249
+ let len = arr_borrow.len();
250
+ let (start_idx, mut acc) = if matches!(initial, Value::Null) && !arr_borrow.is_empty() {
251
+ // No initial value: use first element as acc, start from index 1
252
+ (1, arr_borrow[0].clone())
253
+ } else {
254
+ (0, initial.clone())
255
+ };
256
+ for i in start_idx..len {
257
+ let v = arr_borrow[i].clone();
258
+ acc = cb(&[acc, v.clone(), Value::Number(i as f64)]);
259
+ }
260
+ acc
261
+ } else {
262
+ Value::Null
263
+ }
264
+ }
265
+
266
+ pub fn for_each(arr: &Value, callback: &Value) -> Value {
267
+ if let (Value::Array(arr), Value::Function(cb)) = (arr, callback) {
268
+ let arr_borrow = arr.borrow();
269
+ for (i, v) in arr_borrow.iter().enumerate() {
270
+ cb(&[v.clone(), Value::Number(i as f64)]);
271
+ }
272
+ }
273
+ Value::Null
274
+ }
275
+
276
+ pub fn find(arr: &Value, callback: &Value) -> Value {
277
+ if let (Value::Array(arr), Value::Function(cb)) = (arr, callback) {
278
+ let arr_borrow = arr.borrow();
279
+ for (i, v) in arr_borrow.iter().enumerate() {
280
+ let result = cb(&[v.clone(), Value::Number(i as f64)]);
281
+ if result.is_truthy() {
282
+ return v.clone();
283
+ }
284
+ }
285
+ }
286
+ Value::Null
287
+ }
288
+
289
+ pub fn find_index(arr: &Value, callback: &Value) -> Value {
290
+ if let (Value::Array(arr), Value::Function(cb)) = (arr, callback) {
291
+ let arr_borrow = arr.borrow();
292
+ for (i, v) in arr_borrow.iter().enumerate() {
293
+ let result = cb(&[v.clone(), Value::Number(i as f64)]);
294
+ if result.is_truthy() {
295
+ return Value::Number(i as f64);
296
+ }
297
+ }
298
+ }
299
+ Value::Number(-1.0)
300
+ }
301
+
302
+ pub fn some(arr: &Value, callback: &Value) -> Value {
303
+ if let (Value::Array(arr), Value::Function(cb)) = (arr, callback) {
304
+ let arr_borrow = arr.borrow();
305
+ for (i, v) in arr_borrow.iter().enumerate() {
306
+ let result = cb(&[v.clone(), Value::Number(i as f64)]);
307
+ if result.is_truthy() {
308
+ return Value::Bool(true);
309
+ }
310
+ }
311
+ }
312
+ Value::Bool(false)
313
+ }
314
+
315
+ pub fn every(arr: &Value, callback: &Value) -> Value {
316
+ if let (Value::Array(arr), Value::Function(cb)) = (arr, callback) {
317
+ let arr_borrow = arr.borrow();
318
+ for (i, v) in arr_borrow.iter().enumerate() {
319
+ let result = cb(&[v.clone(), Value::Number(i as f64)]);
320
+ if !result.is_truthy() {
321
+ return Value::Bool(false);
322
+ }
323
+ }
324
+ Value::Bool(true)
325
+ } else {
326
+ Value::Bool(false)
327
+ }
328
+ }
329
+
330
+ pub fn flat_map(arr: &Value, callback: &Value) -> Value {
331
+ if let (Value::Array(arr), Value::Function(cb)) = (arr, callback) {
332
+ let arr_borrow = arr.borrow();
333
+ let mut result: Vec<Value> = Vec::new();
334
+ for (i, v) in arr_borrow.iter().enumerate() {
335
+ let mapped = cb(&[v.clone(), Value::Number(i as f64)]);
336
+ if let Value::Array(inner) = mapped {
337
+ result.extend(inner.borrow().iter().cloned());
338
+ } else {
339
+ result.push(mapped);
340
+ }
341
+ }
342
+ Value::Array(VmRef::new(result))
343
+ } else {
344
+ Value::Null
345
+ }
346
+ }
347
+
348
+ fn sort_by_impl<F>(arr: &Value, cmp: F) -> Value
349
+ where
350
+ F: FnMut(&Value, &Value) -> std::cmp::Ordering,
351
+ {
352
+ if let Value::Array(arr) = arr {
353
+ arr.borrow_mut().sort_by(cmp);
354
+ Value::Array(arr.clone())
355
+ } else {
356
+ Value::Null
357
+ }
358
+ }
359
+
360
+ pub fn sort_default(arr: &Value) -> Value {
361
+ sort_by_impl(arr, |a, b| {
362
+ a.to_display_string().cmp(&b.to_display_string())
363
+ })
364
+ }
365
+
366
+ pub fn sort_with_comparator(arr: &Value, comparator: &Value) -> Value {
367
+ if let (Value::Array(arr), Value::Function(cmp_fn)) = (arr, comparator) {
368
+ let mut arr_mut = arr.borrow_mut();
369
+ let len = arr_mut.len();
370
+ let mut indices: Vec<usize> = (0..len).collect();
371
+ let mut elements: Vec<Value> = std::mem::take(&mut *arr_mut);
372
+ let mut args_buf: [Value; 2] = [Value::Null, Value::Null];
373
+
374
+ indices.sort_by(|&a, &b| {
375
+ args_buf[0] = elements[a].clone();
376
+ args_buf[1] = elements[b].clone();
377
+ match cmp_fn(&args_buf) {
378
+ Value::Number(n) if n < 0.0 => std::cmp::Ordering::Less,
379
+ Value::Number(n) if n > 0.0 => std::cmp::Ordering::Greater,
380
+ _ => std::cmp::Ordering::Equal,
381
+ }
382
+ });
383
+
384
+ *arr_mut = indices
385
+ .into_iter()
386
+ .map(|i| std::mem::replace(&mut elements[i], Value::Null))
387
+ .collect();
388
+ drop(arr_mut);
389
+ Value::Array(arr.clone())
390
+ } else {
391
+ Value::Null
392
+ }
393
+ }
394
+
395
+ fn num_cmp(a: &Value, b: &Value, asc: bool) -> std::cmp::Ordering {
396
+ let (na, nb) = match (a, b) {
397
+ (Value::Number(a), Value::Number(b)) => (*a, *b),
398
+ _ => (f64::NAN, f64::NAN),
399
+ };
400
+ let cmp = na.partial_cmp(&nb).unwrap_or(std::cmp::Ordering::Equal);
401
+ if asc {
402
+ cmp
403
+ } else {
404
+ cmp.reverse()
405
+ }
406
+ }
407
+
408
+ pub fn sort_numeric_asc(arr: &Value) -> Value {
409
+ sort_by_impl(arr, |a, b| num_cmp(a, b, true))
410
+ }
411
+
412
+ pub fn sort_numeric_desc(arr: &Value) -> Value {
413
+ sort_by_impl(arr, |a, b| num_cmp(a, b, false))
414
+ }
415
+
416
+ /// Sort array of objects by numeric property: arr.sort((a,b)=>a.prop-b.prop)
417
+ pub fn sort_by_property_numeric(arr: &Value, prop: &str, asc: bool) -> Value {
418
+ let prop_arc = std::sync::Arc::from(prop);
419
+ sort_by_impl(arr, move |a, b| {
420
+ let na = get_prop_number(a, &prop_arc);
421
+ let nb = get_prop_number(b, &prop_arc);
422
+ let cmp = na.partial_cmp(&nb).unwrap_or(std::cmp::Ordering::Equal);
423
+ if asc {
424
+ cmp
425
+ } else {
426
+ cmp.reverse()
427
+ }
428
+ })
429
+ }
430
+
431
+ fn get_prop_number(v: &Value, prop: &std::sync::Arc<str>) -> f64 {
432
+ match v {
433
+ Value::Object(o) => o
434
+ .borrow()
435
+ .strings
436
+ .get(prop.as_ref())
437
+ .map(|v| v.as_number().unwrap_or(f64::NAN))
438
+ .unwrap_or(f64::NAN),
439
+ _ => f64::NAN,
440
+ }
441
+ }
@@ -0,0 +1,159 @@
1
+ //! `new` lowering for non-JS targets: `construct(callee, args)` approximates JS `[[Construct]]`.
2
+ //! Browser-exact behavior remains on `tish build --target js`.
3
+
4
+ use std::sync::Arc;
5
+ use tishlang_core::{ObjectMap, Value, VmRef};
6
+
7
+ const CONSTRUCT: &str = "__construct";
8
+
9
+ /// Host `new`: `Object` with `__construct`, `Function` as plain call, else `Null`.
10
+ pub fn construct(callee: &Value, args: &[Value]) -> Value {
11
+ match callee {
12
+ Value::Function(f) => f(args),
13
+ Value::Object(o) => {
14
+ let b = o.borrow();
15
+ if let Some(Value::Function(ctor)) = b.strings.get(&Arc::from(CONSTRUCT)) {
16
+ let c = ctor.clone();
17
+ drop(b);
18
+ return c(args);
19
+ }
20
+ Value::Null
21
+ }
22
+ _ => Value::Null,
23
+ }
24
+ }
25
+
26
+ fn param(initial: f64) -> Value {
27
+ let mut m = ObjectMap::default();
28
+ m.insert(Arc::from("value"), Value::Number(initial));
29
+ Value::object(m)
30
+ }
31
+
32
+ fn connect_fn() -> Value {
33
+ Value::native(|_| Value::Null)
34
+ }
35
+
36
+ /// Shared audio-node shape: connect, gain, optional filter fields.
37
+ fn audio_node_stub() -> Value {
38
+ let mut m = ObjectMap::default();
39
+ m.insert(Arc::from("connect"), connect_fn());
40
+ m.insert(Arc::from("gain"), param(0.0));
41
+ m.insert(Arc::from("frequency"), param(440.0));
42
+ m.insert(Arc::from("Q"), param(1.0));
43
+ m.insert(Arc::from("type"), Value::String("peaking".into()));
44
+ Value::object(m)
45
+ }
46
+
47
+ fn analyser_stub() -> Value {
48
+ let mut m = ObjectMap::default();
49
+ m.insert(Arc::from("connect"), connect_fn());
50
+ m.insert(Arc::from("fftSize"), Value::Number(2048.0));
51
+ Value::object(m)
52
+ }
53
+
54
+ fn stereo_panner_stub() -> Value {
55
+ let mut m = ObjectMap::default();
56
+ m.insert(Arc::from("connect"), connect_fn());
57
+ m.insert(Arc::from("pan"), param(0.0));
58
+ Value::object(m)
59
+ }
60
+
61
+ fn audio_buffer_stub(len: usize) -> Value {
62
+ let n = len.max(1);
63
+ let data = VmRef::new(vec![Value::Number(0.0); n]);
64
+ let data2 = data.clone();
65
+ let mut m = ObjectMap::default();
66
+ m.insert(
67
+ Arc::from("getChannelData"),
68
+ Value::native(move |_args| Value::Array(data2.clone())),
69
+ );
70
+ Value::object(m)
71
+ }
72
+
73
+ fn buffer_source_stub() -> Value {
74
+ let mut m = ObjectMap::default();
75
+ m.insert(Arc::from("buffer"), Value::Null);
76
+ m.insert(Arc::from("loop"), Value::Bool(false));
77
+ m.insert(Arc::from("connect"), connect_fn());
78
+ m.insert(Arc::from("start"), Value::native(|_| Value::Null));
79
+ m.insert(Arc::from("stop"), Value::native(|_| Value::Null));
80
+ Value::object(m)
81
+ }
82
+
83
+ fn oscillator_stub() -> Value {
84
+ let mut m = ObjectMap::default();
85
+ m.insert(Arc::from("frequency"), param(440.0));
86
+ m.insert(Arc::from("type"), Value::String("sine".into()));
87
+ m.insert(Arc::from("connect"), connect_fn());
88
+ m.insert(Arc::from("start"), Value::native(|_| Value::Null));
89
+ m.insert(Arc::from("stop"), Value::native(|_| Value::Null));
90
+ Value::object(m)
91
+ }
92
+
93
+ fn audio_context_instance() -> Value {
94
+ let mut ctx = ObjectMap::default();
95
+ ctx.insert(Arc::from("sampleRate"), Value::Number(48_000.0));
96
+ ctx.insert(Arc::from("destination"), audio_node_stub());
97
+
98
+ ctx.insert(
99
+ Arc::from("createGain"),
100
+ Value::native(|_| audio_node_stub()),
101
+ );
102
+ ctx.insert(
103
+ Arc::from("createBiquadFilter"),
104
+ Value::native(|_| audio_node_stub()),
105
+ );
106
+ ctx.insert(
107
+ Arc::from("createStereoPanner"),
108
+ Value::native(|_| stereo_panner_stub()),
109
+ );
110
+ ctx.insert(
111
+ Arc::from("createAnalyser"),
112
+ Value::native(|_| analyser_stub()),
113
+ );
114
+ ctx.insert(
115
+ Arc::from("createBuffer"),
116
+ Value::native(|args: &[Value]| {
117
+ let len = args
118
+ .get(1)
119
+ .and_then(Value::as_number)
120
+ .unwrap_or(0.0)
121
+ .clamp(0.0, 1_000_000_000.0) as usize;
122
+ audio_buffer_stub(len)
123
+ }),
124
+ );
125
+ ctx.insert(
126
+ Arc::from("createBufferSource"),
127
+ Value::native(|_| buffer_source_stub()),
128
+ );
129
+ ctx.insert(
130
+ Arc::from("createOscillator"),
131
+ Value::native(|_| oscillator_stub()),
132
+ );
133
+ ctx.insert(Arc::from("decodeAudioData"), Value::native(|_| Value::Null));
134
+
135
+ Value::object(ctx)
136
+ }
137
+
138
+ /// Global `Uint8Array` for native/VM: `new Uint8Array(n)` → numeric array of zeros (not real bytes).
139
+ pub fn uint8_array_constructor_value() -> Value {
140
+ let ctor = Value::native(|args: &[Value]| {
141
+ let len = args
142
+ .first()
143
+ .and_then(Value::as_number)
144
+ .unwrap_or(0.0)
145
+ .clamp(0.0, 1_000_000_000.0) as usize;
146
+ Value::Array(VmRef::new(vec![Value::Number(0.0); len]))
147
+ });
148
+ let mut m = ObjectMap::default();
149
+ m.insert(Arc::from(CONSTRUCT), ctor);
150
+ Value::object(m)
151
+ }
152
+
153
+ /// Global `AudioContext` for native/VM: stub graph (no real audio).
154
+ pub fn audio_context_constructor_value() -> Value {
155
+ let ctor = Value::native(|_args: &[Value]| audio_context_instance());
156
+ let mut m = ObjectMap::default();
157
+ m.insert(Arc::from(CONSTRUCT), ctor);
158
+ Value::object(m)
159
+ }