@tishlang/tish 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/Cargo.toml +1 -0
  2. package/bin/tish +0 -0
  3. package/crates/js_to_tish/src/error.rs +2 -8
  4. package/crates/js_to_tish/src/transform/expr.rs +101 -130
  5. package/crates/js_to_tish/src/transform/stmt.rs +25 -22
  6. package/crates/tish/Cargo.toml +1 -1
  7. package/crates/tish/src/cli_help.rs +76 -29
  8. package/crates/tish/src/main.rs +85 -54
  9. package/crates/tish/tests/cargo_example_compile.rs +67 -0
  10. package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
  11. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
  12. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
  13. package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
  14. package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
  15. package/crates/tish/tests/integration_test.rs +197 -47
  16. package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
  17. package/crates/tish/tests/shortcircuit.rs +19 -4
  18. package/crates/tish_ast/src/ast.rs +12 -14
  19. package/crates/tish_build_utils/src/lib.rs +64 -6
  20. package/crates/tish_builtins/src/array.rs +52 -21
  21. package/crates/tish_builtins/src/construct.rs +2 -8
  22. package/crates/tish_builtins/src/globals.rs +30 -15
  23. package/crates/tish_builtins/src/lib.rs +5 -5
  24. package/crates/tish_builtins/src/math.rs +5 -3
  25. package/crates/tish_builtins/src/string.rs +71 -19
  26. package/crates/tish_bytecode/src/chunk.rs +0 -1
  27. package/crates/tish_bytecode/src/compiler.rs +164 -60
  28. package/crates/tish_bytecode/src/opcode.rs +13 -4
  29. package/crates/tish_bytecode/src/peephole.rs +2 -2
  30. package/crates/tish_compile/Cargo.toml +1 -0
  31. package/crates/tish_compile/src/codegen.rs +989 -318
  32. package/crates/tish_compile/src/infer.rs +69 -19
  33. package/crates/tish_compile/src/lib.rs +21 -8
  34. package/crates/tish_compile/src/resolve.rs +515 -94
  35. package/crates/tish_compile/src/types.rs +10 -14
  36. package/crates/tish_compile_js/src/codegen.rs +34 -13
  37. package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
  38. package/crates/tish_compiler_wasm/src/lib.rs +16 -13
  39. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +40 -48
  40. package/crates/tish_core/src/json.rs +5 -3
  41. package/crates/tish_core/src/lib.rs +1 -1
  42. package/crates/tish_core/src/uri.rs +9 -6
  43. package/crates/tish_core/src/value.rs +92 -28
  44. package/crates/tish_cranelift/src/link.rs +6 -9
  45. package/crates/tish_cranelift/src/lower.rs +14 -8
  46. package/crates/tish_eval/src/eval.rs +398 -141
  47. package/crates/tish_eval/src/lib.rs +10 -6
  48. package/crates/tish_eval/src/natives.rs +95 -38
  49. package/crates/tish_eval/src/promise.rs +14 -8
  50. package/crates/tish_eval/src/timers.rs +28 -19
  51. package/crates/tish_eval/src/value.rs +10 -3
  52. package/crates/tish_fmt/src/lib.rs +29 -13
  53. package/crates/tish_lexer/src/lib.rs +217 -63
  54. package/crates/tish_lexer/src/token.rs +6 -6
  55. package/crates/tish_llvm/src/lib.rs +15 -8
  56. package/crates/tish_lsp/src/main.rs +41 -43
  57. package/crates/tish_native/src/build.rs +38 -15
  58. package/crates/tish_native/src/lib.rs +76 -32
  59. package/crates/tish_opt/src/lib.rs +67 -50
  60. package/crates/tish_parser/src/lib.rs +36 -11
  61. package/crates/tish_parser/src/parser.rs +172 -87
  62. package/crates/tish_runtime/src/http.rs +15 -6
  63. package/crates/tish_runtime/src/http_fetch.rs +24 -14
  64. package/crates/tish_runtime/src/lib.rs +224 -168
  65. package/crates/tish_runtime/src/promise.rs +1 -5
  66. package/crates/tish_runtime/src/ws.rs +45 -20
  67. package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
  68. package/crates/tish_ui/src/jsx.rs +41 -22
  69. package/crates/tish_ui/src/lib.rs +2 -2
  70. package/crates/tish_vm/src/vm.rs +320 -116
  71. package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +8 -3
  72. package/crates/tish_wasm/src/lib.rs +38 -28
  73. package/crates/tishlang_cargo_bindgen/Cargo.toml +25 -0
  74. package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
  75. package/crates/tishlang_cargo_bindgen/src/discover.rs +52 -0
  76. package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
  77. package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
  78. package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
  79. package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
  80. package/package.json +1 -1
  81. package/platform/darwin-arm64/tish +0 -0
  82. package/platform/darwin-x64/tish +0 -0
  83. package/platform/linux-arm64/tish +0 -0
  84. package/platform/linux-x64/tish +0 -0
  85. package/platform/win32-x64/tish.exe +0 -0
@@ -8,9 +8,9 @@ use std::sync::Arc;
8
8
  use tishlang_ast::{BinOp, UnaryOp};
9
9
  use tishlang_builtins::array as arr_builtins;
10
10
  use tishlang_builtins::construct as construct_builtin;
11
- use tishlang_builtins::string as str_builtins;
12
11
  use tishlang_builtins::globals as globals_builtins;
13
12
  use tishlang_builtins::math as math_builtins;
13
+ use tishlang_builtins::string as str_builtins;
14
14
  use tishlang_bytecode::{u8_to_binop, u8_to_unaryop, Chunk, Constant, Opcode, NO_REST_PARAM};
15
15
  use tishlang_core::{ObjectMap, Value};
16
16
 
@@ -51,12 +51,24 @@ fn get_builtin_export(enabled: &HashSet<String>, spec: &str, export_name: &str)
51
51
  #[cfg(feature = "fs")]
52
52
  if spec == "tish:fs" && cap_allows(enabled, "fs") {
53
53
  return match export_name {
54
- "readFile" => Some(Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::read_file(args)))),
55
- "writeFile" => Some(Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::write_file(args)))),
56
- "fileExists" => Some(Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::file_exists(args)))),
57
- "isDir" => Some(Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::is_dir(args)))),
58
- "readDir" => Some(Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::read_dir(args)))),
59
- "mkdir" => Some(Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::mkdir(args)))),
54
+ "readFile" => Some(Value::Function(Rc::new(|args: &[Value]| {
55
+ tishlang_runtime::read_file(args)
56
+ }))),
57
+ "writeFile" => Some(Value::Function(Rc::new(|args: &[Value]| {
58
+ tishlang_runtime::write_file(args)
59
+ }))),
60
+ "fileExists" => Some(Value::Function(Rc::new(|args: &[Value]| {
61
+ tishlang_runtime::file_exists(args)
62
+ }))),
63
+ "isDir" => Some(Value::Function(Rc::new(|args: &[Value]| {
64
+ tishlang_runtime::is_dir(args)
65
+ }))),
66
+ "readDir" => Some(Value::Function(Rc::new(|args: &[Value]| {
67
+ tishlang_runtime::read_dir(args)
68
+ }))),
69
+ "mkdir" => Some(Value::Function(Rc::new(|args: &[Value]| {
70
+ tishlang_runtime::mkdir(args)
71
+ }))),
60
72
  _ => None,
61
73
  };
62
74
  }
@@ -87,9 +99,15 @@ fn get_builtin_export(enabled: &HashSet<String>, spec: &str, export_name: &str)
87
99
  #[cfg(feature = "process")]
88
100
  if spec == "tish:process" && cap_allows(enabled, "process") {
89
101
  return match export_name {
90
- "exit" => Some(Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_exit(args)))),
91
- "cwd" => Some(Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_cwd(args)))),
92
- "exec" => Some(Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_exec(args)))),
102
+ "exit" => Some(Value::Function(Rc::new(|args: &[Value]| {
103
+ tishlang_runtime::process_exit(args)
104
+ }))),
105
+ "cwd" => Some(Value::Function(Rc::new(|args: &[Value]| {
106
+ tishlang_runtime::process_cwd(args)
107
+ }))),
108
+ "exec" => Some(Value::Function(Rc::new(|args: &[Value]| {
109
+ tishlang_runtime::process_exec(args)
110
+ }))),
93
111
  "argv" => Some(Value::Array(Rc::new(RefCell::new(
94
112
  std::env::args().map(|s| Value::String(s.into())).collect(),
95
113
  )))),
@@ -100,9 +118,24 @@ fn get_builtin_export(enabled: &HashSet<String>, spec: &str, export_name: &str)
100
118
  )))),
101
119
  "process" => {
102
120
  let mut m = ObjectMap::default();
103
- m.insert("exit".into(), Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_exit(args))));
104
- m.insert("cwd".into(), Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_cwd(args))));
105
- m.insert("exec".into(), Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_exec(args))));
121
+ m.insert(
122
+ "exit".into(),
123
+ Value::Function(Rc::new(|args: &[Value]| {
124
+ tishlang_runtime::process_exit(args)
125
+ })),
126
+ );
127
+ m.insert(
128
+ "cwd".into(),
129
+ Value::Function(Rc::new(|args: &[Value]| {
130
+ tishlang_runtime::process_cwd(args)
131
+ })),
132
+ );
133
+ m.insert(
134
+ "exec".into(),
135
+ Value::Function(Rc::new(|args: &[Value]| {
136
+ tishlang_runtime::process_exec(args)
137
+ })),
138
+ );
106
139
  m.insert(
107
140
  "argv".into(),
108
141
  Value::Array(Rc::new(RefCell::new(
@@ -134,7 +167,10 @@ fn get_builtin_export(enabled: &HashSet<String>, spec: &str, export_name: &str)
134
167
  "wsSend" => Some(Value::Function(Rc::new(|args: &[Value]| {
135
168
  Value::Bool(tishlang_runtime::ws_send_native(
136
169
  args.first().unwrap_or(&Value::Null),
137
- &args.get(1).map(|v| v.to_display_string()).unwrap_or_default(),
170
+ &args
171
+ .get(1)
172
+ .map(|v| v.to_display_string())
173
+ .unwrap_or_default(),
138
174
  ))
139
175
  }))),
140
176
  "wsBroadcast" => Some(Value::Function(Rc::new(|args: &[Value]| {
@@ -183,7 +219,8 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
183
219
  console.insert(
184
220
  "debug".into(),
185
221
  Value::Function(Rc::new(|args: &[Value]| {
186
- let s = tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
222
+ let s =
223
+ tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
187
224
  vm_log(&s);
188
225
  Value::Null
189
226
  })),
@@ -191,7 +228,8 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
191
228
  console.insert(
192
229
  "log".into(),
193
230
  Value::Function(Rc::new(|args: &[Value]| {
194
- let s = tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
231
+ let s =
232
+ tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
195
233
  vm_log(&s);
196
234
  Value::Null
197
235
  })),
@@ -199,7 +237,8 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
199
237
  console.insert(
200
238
  "info".into(),
201
239
  Value::Function(Rc::new(|args: &[Value]| {
202
- let s = tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
240
+ let s =
241
+ tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
203
242
  vm_log(&s);
204
243
  Value::Null
205
244
  })),
@@ -207,7 +246,8 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
207
246
  console.insert(
208
247
  "warn".into(),
209
248
  Value::Function(Rc::new(|args: &[Value]| {
210
- let s = tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
249
+ let s =
250
+ tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
211
251
  vm_log_err(&s);
212
252
  Value::Null
213
253
  })),
@@ -215,12 +255,16 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
215
255
  console.insert(
216
256
  "error".into(),
217
257
  Value::Function(Rc::new(|args: &[Value]| {
218
- let s = tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
258
+ let s =
259
+ tishlang_core::format_values_for_console(args, tishlang_core::use_console_colors());
219
260
  vm_log_err(&s);
220
261
  Value::Null
221
262
  })),
222
263
  );
223
- g.insert("console".into(), Value::Object(Rc::new(RefCell::new(console))));
264
+ g.insert(
265
+ "console".into(),
266
+ Value::Object(Rc::new(RefCell::new(console))),
267
+ );
224
268
 
225
269
  let mut math = ObjectMap::default();
226
270
  math.insert(
@@ -276,14 +320,38 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
276
320
  Value::Number(nums.into_iter().fold(f64::NAN, |a, b| a.max(b)))
277
321
  })),
278
322
  );
279
- math.insert("pow".into(), Value::Function(Rc::new(|args: &[Value]| math_builtins::pow(args))));
280
- math.insert("sin".into(), Value::Function(Rc::new(|args: &[Value]| math_builtins::sin(args))));
281
- math.insert("cos".into(), Value::Function(Rc::new(|args: &[Value]| math_builtins::cos(args))));
282
- math.insert("tan".into(), Value::Function(Rc::new(|args: &[Value]| math_builtins::tan(args))));
283
- math.insert("log".into(), Value::Function(Rc::new(|args: &[Value]| math_builtins::log(args))));
284
- math.insert("exp".into(), Value::Function(Rc::new(|args: &[Value]| math_builtins::exp(args))));
285
- math.insert("sign".into(), Value::Function(Rc::new(|args: &[Value]| math_builtins::sign(args))));
286
- math.insert("trunc".into(), Value::Function(Rc::new(|args: &[Value]| math_builtins::trunc(args))));
323
+ math.insert(
324
+ "pow".into(),
325
+ Value::Function(Rc::new(|args: &[Value]| math_builtins::pow(args))),
326
+ );
327
+ math.insert(
328
+ "sin".into(),
329
+ Value::Function(Rc::new(|args: &[Value]| math_builtins::sin(args))),
330
+ );
331
+ math.insert(
332
+ "cos".into(),
333
+ Value::Function(Rc::new(|args: &[Value]| math_builtins::cos(args))),
334
+ );
335
+ math.insert(
336
+ "tan".into(),
337
+ Value::Function(Rc::new(|args: &[Value]| math_builtins::tan(args))),
338
+ );
339
+ math.insert(
340
+ "log".into(),
341
+ Value::Function(Rc::new(|args: &[Value]| math_builtins::log(args))),
342
+ );
343
+ math.insert(
344
+ "exp".into(),
345
+ Value::Function(Rc::new(|args: &[Value]| math_builtins::exp(args))),
346
+ );
347
+ math.insert(
348
+ "sign".into(),
349
+ Value::Function(Rc::new(|args: &[Value]| math_builtins::sign(args))),
350
+ );
351
+ math.insert(
352
+ "trunc".into(),
353
+ Value::Function(Rc::new(|args: &[Value]| math_builtins::trunc(args))),
354
+ );
287
355
  math.insert("PI".into(), Value::Number(std::f64::consts::PI));
288
356
  math.insert("E".into(), Value::Number(std::f64::consts::E));
289
357
  g.insert("Math".into(), Value::Object(Rc::new(RefCell::new(math))));
@@ -292,7 +360,10 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
292
360
  json.insert(
293
361
  "parse".into(),
294
362
  Value::Function(Rc::new(|args: &[Value]| {
295
- let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
363
+ let s = args
364
+ .first()
365
+ .map(|v| v.to_display_string())
366
+ .unwrap_or_default();
296
367
  tishlang_core::json_parse(&s).unwrap_or(Value::Null)
297
368
  })),
298
369
  );
@@ -305,13 +376,36 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
305
376
  );
306
377
  g.insert("JSON".into(), Value::Object(Rc::new(RefCell::new(json))));
307
378
 
308
- g.insert("parseInt".into(), Value::Function(Rc::new(|args: &[Value]| globals_builtins::parse_int(args))));
309
- g.insert("parseFloat".into(), Value::Function(Rc::new(|args: &[Value]| globals_builtins::parse_float(args))));
310
- g.insert("encodeURI".into(), Value::Function(Rc::new(|args: &[Value]| globals_builtins::encode_uri(args))));
311
- g.insert("decodeURI".into(), Value::Function(Rc::new(|args: &[Value]| globals_builtins::decode_uri(args))));
312
- g.insert("Boolean".into(), Value::Function(Rc::new(|args: &[Value]| globals_builtins::boolean(args))));
313
- g.insert("isFinite".into(), Value::Function(Rc::new(|args: &[Value]| globals_builtins::is_finite(args))));
314
- g.insert("isNaN".into(), Value::Function(Rc::new(|args: &[Value]| globals_builtins::is_nan(args))));
379
+ g.insert(
380
+ "parseInt".into(),
381
+ Value::Function(Rc::new(|args: &[Value]| globals_builtins::parse_int(args))),
382
+ );
383
+ g.insert(
384
+ "parseFloat".into(),
385
+ Value::Function(Rc::new(|args: &[Value]| {
386
+ globals_builtins::parse_float(args)
387
+ })),
388
+ );
389
+ g.insert(
390
+ "encodeURI".into(),
391
+ Value::Function(Rc::new(|args: &[Value]| globals_builtins::encode_uri(args))),
392
+ );
393
+ g.insert(
394
+ "decodeURI".into(),
395
+ Value::Function(Rc::new(|args: &[Value]| globals_builtins::decode_uri(args))),
396
+ );
397
+ g.insert(
398
+ "Boolean".into(),
399
+ Value::Function(Rc::new(|args: &[Value]| globals_builtins::boolean(args))),
400
+ );
401
+ g.insert(
402
+ "isFinite".into(),
403
+ Value::Function(Rc::new(|args: &[Value]| globals_builtins::is_finite(args))),
404
+ );
405
+ g.insert(
406
+ "isNaN".into(),
407
+ Value::Function(Rc::new(|args: &[Value]| globals_builtins::is_nan(args))),
408
+ );
315
409
  g.insert("Infinity".into(), Value::Number(f64::INFINITY));
316
410
  g.insert("NaN".into(), Value::Number(f64::NAN));
317
411
  g.insert(
@@ -349,31 +443,68 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
349
443
  let mut object_methods = ObjectMap::default();
350
444
  object_methods.insert(
351
445
  "assign".into(),
352
- Value::Function(Rc::new(|args: &[Value]| globals_builtins::object_assign(args))),
446
+ Value::Function(Rc::new(|args: &[Value]| {
447
+ globals_builtins::object_assign(args)
448
+ })),
353
449
  );
354
450
  object_methods.insert(
355
451
  "fromEntries".into(),
356
- Value::Function(Rc::new(|args: &[Value]| globals_builtins::object_from_entries(args))),
452
+ Value::Function(Rc::new(|args: &[Value]| {
453
+ globals_builtins::object_from_entries(args)
454
+ })),
455
+ );
456
+ object_methods.insert(
457
+ "keys".into(),
458
+ Value::Function(Rc::new(|args: &[Value]| {
459
+ globals_builtins::object_keys(args)
460
+ })),
461
+ );
462
+ object_methods.insert(
463
+ "values".into(),
464
+ Value::Function(Rc::new(|args: &[Value]| {
465
+ globals_builtins::object_values(args)
466
+ })),
467
+ );
468
+ object_methods.insert(
469
+ "entries".into(),
470
+ Value::Function(Rc::new(|args: &[Value]| {
471
+ globals_builtins::object_entries(args)
472
+ })),
473
+ );
474
+ g.insert(
475
+ "Object".into(),
476
+ Value::Object(Rc::new(RefCell::new(object_methods))),
357
477
  );
358
- object_methods.insert("keys".into(), Value::Function(Rc::new(|args: &[Value]| globals_builtins::object_keys(args))));
359
- object_methods.insert("values".into(), Value::Function(Rc::new(|args: &[Value]| globals_builtins::object_values(args))));
360
- object_methods.insert("entries".into(), Value::Function(Rc::new(|args: &[Value]| globals_builtins::object_entries(args))));
361
- g.insert("Object".into(), Value::Object(Rc::new(RefCell::new(object_methods))));
362
478
 
363
479
  // Array.isArray
364
480
  let mut array_static = ObjectMap::default();
365
481
  array_static.insert(
366
482
  "isArray".into(),
367
- Value::Function(Rc::new(|args: &[Value]| globals_builtins::array_is_array(args))),
483
+ Value::Function(Rc::new(|args: &[Value]| {
484
+ globals_builtins::array_is_array(args)
485
+ })),
486
+ );
487
+ g.insert(
488
+ "Array".into(),
489
+ Value::Object(Rc::new(RefCell::new(array_static))),
368
490
  );
369
- g.insert("Array".into(), Value::Object(Rc::new(RefCell::new(array_static))));
370
491
 
371
492
  // String(value) as callable + String.fromCharCode
372
- let string_convert_fn = Value::Function(Rc::new(|args: &[Value]| globals_builtins::string_convert(args)));
493
+ let string_convert_fn = Value::Function(Rc::new(|args: &[Value]| {
494
+ globals_builtins::string_convert(args)
495
+ }));
373
496
  let mut string_static = ObjectMap::default();
374
- string_static.insert("fromCharCode".into(), Value::Function(Rc::new(|args: &[Value]| globals_builtins::string_from_char_code(args))));
497
+ string_static.insert(
498
+ "fromCharCode".into(),
499
+ Value::Function(Rc::new(|args: &[Value]| {
500
+ globals_builtins::string_from_char_code(args)
501
+ })),
502
+ );
375
503
  string_static.insert(Arc::from("__call"), string_convert_fn);
376
- g.insert("String".into(), Value::Object(Rc::new(RefCell::new(string_static))));
504
+ g.insert(
505
+ "String".into(),
506
+ Value::Object(Rc::new(RefCell::new(string_static))),
507
+ );
377
508
 
378
509
  // JSX / Lattish: stubs for bytecode VM when no DOM (e.g. console). Override via set_global in browser.
379
510
  g.insert(
@@ -405,22 +536,31 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
405
536
  );
406
537
  let mut document_obj = ObjectMap::default();
407
538
  document_obj.insert("body".into(), Value::Null);
408
- g.insert("document".into(), Value::Object(Rc::new(RefCell::new(document_obj))));
539
+ g.insert(
540
+ "document".into(),
541
+ Value::Object(Rc::new(RefCell::new(document_obj))),
542
+ );
409
543
 
410
544
  #[cfg(feature = "process")]
411
545
  if cap_allows(enabled, "process") {
412
546
  let mut process_obj = ObjectMap::default();
413
547
  process_obj.insert(
414
548
  "exit".into(),
415
- Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_exit(args))),
549
+ Value::Function(Rc::new(|args: &[Value]| {
550
+ tishlang_runtime::process_exit(args)
551
+ })),
416
552
  );
417
553
  process_obj.insert(
418
554
  "cwd".into(),
419
- Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_cwd(args))),
555
+ Value::Function(Rc::new(|args: &[Value]| {
556
+ tishlang_runtime::process_cwd(args)
557
+ })),
420
558
  );
421
559
  process_obj.insert(
422
560
  "exec".into(),
423
- Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_exec(args))),
561
+ Value::Function(Rc::new(|args: &[Value]| {
562
+ tishlang_runtime::process_exec(args)
563
+ })),
424
564
  );
425
565
  process_obj.insert(
426
566
  "argv".into(),
@@ -436,7 +576,10 @@ fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
436
576
  .collect(),
437
577
  ))),
438
578
  );
439
- g.insert("process".into(), Value::Object(Rc::new(RefCell::new(process_obj))));
579
+ g.insert(
580
+ "process".into(),
581
+ Value::Object(Rc::new(RefCell::new(process_obj))),
582
+ );
440
583
  }
441
584
 
442
585
  #[cfg(feature = "http")]
@@ -511,7 +654,11 @@ impl Vm {
511
654
 
512
655
  /// Names of all globals (for REPL bare-word tab completion).
513
656
  pub fn global_names(&self) -> Vec<String> {
514
- self.globals.borrow().keys().map(|k| k.as_ref().to_string()).collect()
657
+ self.globals
658
+ .borrow()
659
+ .keys()
660
+ .map(|k| k.as_ref().to_string())
661
+ .collect()
515
662
  }
516
663
 
517
664
  fn read_u16(code: &[u8], ip: &mut usize) -> u16 {
@@ -663,9 +810,7 @@ impl Vm {
663
810
  } else if self.scope.contains_key(name.as_ref()) {
664
811
  self.scope.insert(Arc::clone(name), v);
665
812
  } else if self.globals.borrow().contains_key(name.as_ref()) {
666
- self.globals
667
- .borrow_mut()
668
- .insert(Arc::clone(name), v);
813
+ self.globals.borrow_mut().insert(Arc::clone(name), v);
669
814
  } else {
670
815
  // New variable: at top level (no enclosing) store in globals so REPL persists across lines
671
816
  if self.enclosing.is_none() {
@@ -716,9 +861,9 @@ impl Vm {
716
861
  block_undo_stack.push(Vec::new());
717
862
  }
718
863
  Opcode::ExitBlock => {
719
- let frame = block_undo_stack.pop().ok_or_else(|| {
720
- "ExitBlock without matching EnterBlock".to_string()
721
- })?;
864
+ let frame = block_undo_stack
865
+ .pop()
866
+ .ok_or_else(|| "ExitBlock without matching EnterBlock".to_string())?;
722
867
  for (name, old) in frame.into_iter().rev() {
723
868
  let mut ls = local_scope.borrow_mut();
724
869
  match old {
@@ -753,17 +898,19 @@ impl Vm {
753
898
  .stack
754
899
  .pop()
755
900
  .ok_or_else(|| "Stack underflow".to_string())?;
756
- self.globals
757
- .borrow_mut()
758
- .insert(Arc::clone(name), v);
901
+ self.globals.borrow_mut().insert(Arc::clone(name), v);
759
902
  }
760
903
  Opcode::Pop => {
761
- self.stack.pop().ok_or_else(|| "Stack underflow".to_string())?;
904
+ self.stack
905
+ .pop()
906
+ .ok_or_else(|| "Stack underflow".to_string())?;
762
907
  }
763
908
  Opcode::PopN => {
764
909
  let n = Self::read_u16(code, &mut ip) as usize;
765
910
  for _ in 0..n {
766
- self.stack.pop().ok_or_else(|| "Stack underflow".to_string())?;
911
+ self.stack
912
+ .pop()
913
+ .ok_or_else(|| "Stack underflow".to_string())?;
767
914
  }
768
915
  }
769
916
  Opcode::Dup => {
@@ -792,7 +939,9 @@ impl Vm {
792
939
  let f = match &callee {
793
940
  Value::Function(f) => Rc::clone(f),
794
941
  Value::Object(o) => {
795
- if let Some(Value::Function(call_fn)) = o.borrow().get(&Arc::from("__call")) {
942
+ if let Some(Value::Function(call_fn)) =
943
+ o.borrow().get(&Arc::from("__call"))
944
+ {
796
945
  Rc::clone(call_fn)
797
946
  } else {
798
947
  return Err(format!(
@@ -802,10 +951,7 @@ impl Vm {
802
951
  }
803
952
  }
804
953
  _ => {
805
- return Err(format!(
806
- "Call of non-function: {}",
807
- callee.type_name()
808
- ));
954
+ return Err(format!("Call of non-function: {}", callee.type_name()));
809
955
  }
810
956
  };
811
957
  let result = f(&args);
@@ -832,7 +978,9 @@ impl Vm {
832
978
  let f = match &callee {
833
979
  Value::Function(f) => Rc::clone(f),
834
980
  Value::Object(o) => {
835
- if let Some(Value::Function(call_fn)) = o.borrow().get(&Arc::from("__call")) {
981
+ if let Some(Value::Function(call_fn)) =
982
+ o.borrow().get(&Arc::from("__call"))
983
+ {
836
984
  Rc::clone(call_fn)
837
985
  } else {
838
986
  return Err(format!(
@@ -842,10 +990,7 @@ impl Vm {
842
990
  }
843
991
  }
844
992
  _ => {
845
- return Err(format!(
846
- "Call of non-function: {}",
847
- callee.type_name()
848
- ));
993
+ return Err(format!("Call of non-function: {}", callee.type_name()));
849
994
  }
850
995
  };
851
996
  let result = f(&args);
@@ -922,8 +1067,8 @@ impl Vm {
922
1067
  .stack
923
1068
  .pop()
924
1069
  .ok_or_else(|| "Stack underflow".to_string())?;
925
- let op = u8_to_binop(op_u8)
926
- .ok_or_else(|| format!("Unknown binop: {}", op_u8))?;
1070
+ let op =
1071
+ u8_to_binop(op_u8).ok_or_else(|| format!("Unknown binop: {}", op_u8))?;
927
1072
  let result = eval_binop(op, &l, &r)?;
928
1073
  self.stack.push(result);
929
1074
  }
@@ -1023,8 +1168,7 @@ impl Vm {
1023
1168
  );
1024
1169
  }
1025
1170
  elems.reverse();
1026
- self.stack
1027
- .push(Value::Array(Rc::new(RefCell::new(elems))));
1171
+ self.stack.push(Value::Array(Rc::new(RefCell::new(elems))));
1028
1172
  }
1029
1173
  Opcode::NewObject => {
1030
1174
  let n = Self::read_u16(code, &mut ip) as usize;
@@ -1041,8 +1185,7 @@ impl Vm {
1041
1185
  let key = key_val.to_display_string().into();
1042
1186
  map.insert(key, val);
1043
1187
  }
1044
- self.stack
1045
- .push(Value::Object(Rc::new(RefCell::new(map))));
1188
+ self.stack.push(Value::Object(Rc::new(RefCell::new(map))));
1046
1189
  }
1047
1190
  Opcode::EnterTry => {
1048
1191
  let offset = Self::read_u16(code, &mut ip) as usize;
@@ -1159,9 +1302,7 @@ impl Vm {
1159
1302
  .pop()
1160
1303
  .ok_or_else(|| "Stack underflow".to_string())?;
1161
1304
  let result = match &arr {
1162
- Value::Array(a) => {
1163
- Value::Array(Rc::new(RefCell::new(a.borrow().clone())))
1164
- }
1305
+ Value::Array(a) => Value::Array(Rc::new(RefCell::new(a.borrow().clone()))),
1165
1306
  _ => Value::Null,
1166
1307
  };
1167
1308
  self.stack.push(result);
@@ -1187,8 +1328,16 @@ impl Vm {
1187
1328
  let mapped: Vec<Value> = arr_borrow
1188
1329
  .iter()
1189
1330
  .map(|v| {
1190
- let l: Value = if param_left { (*v).clone() } else { const_val.clone() };
1191
- let r: Value = if param_left { const_val.clone() } else { (*v).clone() };
1331
+ let l: Value = if param_left {
1332
+ (*v).clone()
1333
+ } else {
1334
+ const_val.clone()
1335
+ };
1336
+ let r: Value = if param_left {
1337
+ const_val.clone()
1338
+ } else {
1339
+ (*v).clone()
1340
+ };
1192
1341
  eval_binop(binop, &l, &r).unwrap_or(Value::Null)
1193
1342
  })
1194
1343
  .collect();
@@ -1204,8 +1353,9 @@ impl Vm {
1204
1353
  let const_idx = Self::read_u16(code, &mut ip);
1205
1354
  let param_left = code[ip] == 0; // 0 = param on left (x op const), 1 = param on right (const op x)
1206
1355
  ip += 1;
1207
- let binop = u8_to_binop(binop_u8)
1208
- .ok_or_else(|| format!("Unknown binop in ArrayFilterBinOp: {}", binop_u8))?;
1356
+ let binop = u8_to_binop(binop_u8).ok_or_else(|| {
1357
+ format!("Unknown binop in ArrayFilterBinOp: {}", binop_u8)
1358
+ })?;
1209
1359
  let arr = self
1210
1360
  .stack
1211
1361
  .pop()
@@ -1219,8 +1369,16 @@ impl Vm {
1219
1369
  let filtered: Vec<Value> = arr_borrow
1220
1370
  .iter()
1221
1371
  .filter(|v| {
1222
- let l: Value = if param_left { (*v).clone() } else { const_val.clone() };
1223
- let r: Value = if param_left { const_val.clone() } else { (*v).clone() };
1372
+ let l: Value = if param_left {
1373
+ (*v).clone()
1374
+ } else {
1375
+ const_val.clone()
1376
+ };
1377
+ let r: Value = if param_left {
1378
+ const_val.clone()
1379
+ } else {
1380
+ (*v).clone()
1381
+ };
1224
1382
  let b = eval_binop(binop, &l, &r).unwrap_or(Value::Null);
1225
1383
  b.is_truthy()
1226
1384
  })
@@ -1250,7 +1408,10 @@ impl Vm {
1250
1408
  let spec = match constants.get(spec_idx as usize) {
1251
1409
  Some(Constant::String(s)) => s.as_ref(),
1252
1410
  _ => {
1253
- return Err("LoadNativeExport: spec constant out of bounds or not string".to_string());
1411
+ return Err(
1412
+ "LoadNativeExport: spec constant out of bounds or not string"
1413
+ .to_string(),
1414
+ );
1254
1415
  }
1255
1416
  };
1256
1417
  let export_name = match constants.get(export_idx as usize) {
@@ -1260,10 +1421,17 @@ impl Vm {
1260
1421
  }
1261
1422
  };
1262
1423
  let v = get_builtin_export(self.capabilities.as_ref(), spec, export_name).ok_or_else(|| {
1263
- format!(
1264
- "Built-in module '{}' does not export '{}' or capability not enabled for this run. Use e.g. tish run --feature fs (or full). The tish binary must also be built with that capability linked in.",
1265
- spec, export_name
1266
- )
1424
+ if spec.starts_with("cargo:") {
1425
+ format!(
1426
+ "cargo:… imports are only supported by `tish build` with the Rust native backend (not the bytecode VM). Spec: {}",
1427
+ spec
1428
+ )
1429
+ } else {
1430
+ format!(
1431
+ "Built-in module '{}' does not export '{}' or capability not enabled for this run. Use e.g. tish run --feature fs (or full). The tish binary must also be built with that capability linked in.",
1432
+ spec, export_name
1433
+ )
1434
+ }
1267
1435
  })?;
1268
1436
  self.stack.push(v);
1269
1437
  }
@@ -1393,33 +1561,50 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
1393
1561
  match obj {
1394
1562
  Value::Object(m) => {
1395
1563
  let map = m.borrow();
1396
- map.get(key.as_ref()).cloned().ok_or_else(|| {
1397
- format!("Property '{}' not found", key)
1398
- })
1564
+ map.get(key.as_ref())
1565
+ .cloned()
1566
+ .ok_or_else(|| format!("Property '{}' not found", key))
1399
1567
  }
1400
1568
  Value::Array(a) => {
1401
1569
  let key_s = key.as_ref();
1402
1570
  if let Ok(idx) = key_s.parse::<usize>() {
1403
1571
  let arr = a.borrow();
1404
- return arr.get(idx).cloned().ok_or_else(|| "Index out of bounds".to_string());
1572
+ return arr
1573
+ .get(idx)
1574
+ .cloned()
1575
+ .ok_or_else(|| "Index out of bounds".to_string());
1405
1576
  }
1406
1577
  if key_s == "length" {
1407
1578
  return Ok(Value::Number(a.borrow().len() as f64));
1408
1579
  }
1409
1580
  let a_clone = Rc::clone(a);
1410
1581
  let method: ArrayMethodFn = match key_s {
1411
- "push" => Rc::new(move |args: &[Value]| arr_builtins::push(&Value::Array(Rc::clone(&a_clone)), args)),
1412
- "pop" => Rc::new(move |_args: &[Value]| arr_builtins::pop(&Value::Array(Rc::clone(&a_clone)))),
1413
- "shift" => Rc::new(move |_args: &[Value]| arr_builtins::shift(&Value::Array(Rc::clone(&a_clone)))),
1414
- "unshift" => Rc::new(move |args: &[Value]| arr_builtins::unshift(&Value::Array(Rc::clone(&a_clone)), args)),
1415
- "reverse" => Rc::new(move |_args: &[Value]| arr_builtins::reverse(&Value::Array(Rc::clone(&a_clone)))),
1416
- "shuffle" => Rc::new(move |_args: &[Value]| arr_builtins::shuffle(&Value::Array(Rc::clone(&a_clone)))),
1582
+ "push" => Rc::new(move |args: &[Value]| {
1583
+ arr_builtins::push(&Value::Array(Rc::clone(&a_clone)), args)
1584
+ }),
1585
+ "pop" => Rc::new(move |_args: &[Value]| {
1586
+ arr_builtins::pop(&Value::Array(Rc::clone(&a_clone)))
1587
+ }),
1588
+ "shift" => Rc::new(move |_args: &[Value]| {
1589
+ arr_builtins::shift(&Value::Array(Rc::clone(&a_clone)))
1590
+ }),
1591
+ "unshift" => Rc::new(move |args: &[Value]| {
1592
+ arr_builtins::unshift(&Value::Array(Rc::clone(&a_clone)), args)
1593
+ }),
1594
+ "reverse" => Rc::new(move |_args: &[Value]| {
1595
+ arr_builtins::reverse(&Value::Array(Rc::clone(&a_clone)))
1596
+ }),
1597
+ "shuffle" => Rc::new(move |_args: &[Value]| {
1598
+ arr_builtins::shuffle(&Value::Array(Rc::clone(&a_clone)))
1599
+ }),
1417
1600
  "slice" => Rc::new(move |args: &[Value]| {
1418
1601
  let start = args.first().unwrap_or(&Value::Null);
1419
1602
  let end = args.get(1).unwrap_or(&Value::Null);
1420
1603
  arr_builtins::slice(&Value::Array(Rc::clone(&a_clone)), start, end)
1421
1604
  }),
1422
- "concat" => Rc::new(move |args: &[Value]| arr_builtins::concat(&Value::Array(Rc::clone(&a_clone)), args)),
1605
+ "concat" => Rc::new(move |args: &[Value]| {
1606
+ arr_builtins::concat(&Value::Array(Rc::clone(&a_clone)), args)
1607
+ }),
1423
1608
  "join" => Rc::new(move |args: &[Value]| {
1424
1609
  let sep = args.first().unwrap_or(&Value::Null);
1425
1610
  arr_builtins::join(&Value::Array(Rc::clone(&a_clone)), sep)
@@ -1477,7 +1662,10 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
1477
1662
  "sort" => Rc::new(move |args: &[Value]| {
1478
1663
  let cmp = args.first();
1479
1664
  if let Some(Value::Function(_)) = cmp {
1480
- arr_builtins::sort_with_comparator(&Value::Array(Rc::clone(&a_clone)), cmp.unwrap())
1665
+ arr_builtins::sort_with_comparator(
1666
+ &Value::Array(Rc::clone(&a_clone)),
1667
+ cmp.unwrap(),
1668
+ )
1481
1669
  } else {
1482
1670
  arr_builtins::sort_default(&Value::Array(Rc::clone(&a_clone)))
1483
1671
  }
@@ -1486,7 +1674,12 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
1486
1674
  let start = args.first().unwrap_or(&Value::Null);
1487
1675
  let delete_count = args.get(1).map(|v| v as &Value);
1488
1676
  let items: Vec<Value> = args.get(2..).unwrap_or(&[]).to_vec();
1489
- arr_builtins::splice(&Value::Array(Rc::clone(&a_clone)), start, delete_count, &items)
1677
+ arr_builtins::splice(
1678
+ &Value::Array(Rc::clone(&a_clone)),
1679
+ start,
1680
+ delete_count,
1681
+ &items,
1682
+ )
1490
1683
  }),
1491
1684
  _ => return Err(format!("Property '{}' not found", key)),
1492
1685
  };
@@ -1512,10 +1705,7 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
1512
1705
  }),
1513
1706
  "lastIndexOf" => Rc::new(move |args: &[Value]| {
1514
1707
  let search = args.first().unwrap_or(&Value::Null);
1515
- let position = args
1516
- .get(1)
1517
- .cloned()
1518
- .unwrap_or(Value::Number(f64::INFINITY));
1708
+ let position = args.get(1).cloned().unwrap_or(Value::Number(f64::INFINITY));
1519
1709
  str_builtins::last_index_of(
1520
1710
  &Value::String(Arc::clone(&s_clone)),
1521
1711
  search,
@@ -1541,9 +1731,15 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
1541
1731
  let sep = args.first().unwrap_or(&Value::Null);
1542
1732
  str_builtins::split(&Value::String(Arc::clone(&s_clone)), sep)
1543
1733
  }),
1544
- "trim" => Rc::new(move |_args: &[Value]| str_builtins::trim(&Value::String(Arc::clone(&s_clone)))),
1545
- "toUpperCase" => Rc::new(move |_args: &[Value]| str_builtins::to_upper_case(&Value::String(Arc::clone(&s_clone)))),
1546
- "toLowerCase" => Rc::new(move |_args: &[Value]| str_builtins::to_lower_case(&Value::String(Arc::clone(&s_clone)))),
1734
+ "trim" => Rc::new(move |_args: &[Value]| {
1735
+ str_builtins::trim(&Value::String(Arc::clone(&s_clone)))
1736
+ }),
1737
+ "toUpperCase" => Rc::new(move |_args: &[Value]| {
1738
+ str_builtins::to_upper_case(&Value::String(Arc::clone(&s_clone)))
1739
+ }),
1740
+ "toLowerCase" => Rc::new(move |_args: &[Value]| {
1741
+ str_builtins::to_lower_case(&Value::String(Arc::clone(&s_clone)))
1742
+ }),
1547
1743
  "startsWith" => Rc::new(move |args: &[Value]| {
1548
1744
  let search = args.first().unwrap_or(&Value::Null);
1549
1745
  str_builtins::starts_with(&Value::String(Arc::clone(&s_clone)), search)
@@ -1560,7 +1756,11 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
1560
1756
  "replaceAll" => Rc::new(move |args: &[Value]| {
1561
1757
  let search = args.first().unwrap_or(&Value::Null);
1562
1758
  let replacement = args.get(1).unwrap_or(&Value::Null);
1563
- str_builtins::replace_all(&Value::String(Arc::clone(&s_clone)), search, replacement)
1759
+ str_builtins::replace_all(
1760
+ &Value::String(Arc::clone(&s_clone)),
1761
+ search,
1762
+ replacement,
1763
+ )
1564
1764
  }),
1565
1765
  "charAt" => Rc::new(move |args: &[Value]| {
1566
1766
  let idx = args.first().unwrap_or(&Value::Null);
@@ -1588,7 +1788,11 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
1588
1788
  };
1589
1789
  Ok(Value::Function(method))
1590
1790
  }
1591
- _ => Err(format!("Cannot read property '{}' of {}", key, obj.type_name())),
1791
+ _ => Err(format!(
1792
+ "Cannot read property '{}' of {}",
1793
+ key,
1794
+ obj.type_name()
1795
+ )),
1592
1796
  }
1593
1797
  }
1594
1798