@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,318 @@
1
+ //! Runtime values for the Tish interpreter.
2
+ //!
3
+ //! This module defines the interpreter's `Value` type, which includes variants
4
+ //! like `Function`, `Native`, and `Serve` that hold AST or interpreter-specific
5
+ //! data. The compiled runtime uses `tishlang_core::Value` instead, which has a
6
+ //! different shape (no AST-carrying variants). The split is intentional.
7
+
8
+ use std::cell::RefCell;
9
+ use std::rc::Rc;
10
+ use std::sync::Arc;
11
+
12
+ use ahash::AHashMap;
13
+ use tishlang_ast::{FunParam, Statement};
14
+ use tishlang_core::NativeFn as CoreNativeFn;
15
+ use tishlang_core::TishSymbol;
16
+
17
+ /// Property map for interpreter object string keys (uses `eval::Value`, not `tishlang_core::Value`).
18
+ pub type PropMap = AHashMap<Arc<str>, Value>;
19
+
20
+ /// Interpreter object: string keys plus optional symbol-keyed properties.
21
+ #[derive(Clone, Debug, Default)]
22
+ pub struct EvalObjectData {
23
+ pub strings: PropMap,
24
+ pub symbols: Option<AHashMap<u64, Value>>,
25
+ }
26
+
27
+ impl EvalObjectData {
28
+ pub fn from_strings(strings: PropMap) -> Self {
29
+ Self {
30
+ strings,
31
+ symbols: None,
32
+ }
33
+ }
34
+ }
35
+
36
+ pub fn eval_object_get(obj: &Value, key: &Value) -> Option<Value> {
37
+ let Value::Object(od) = obj else {
38
+ return None;
39
+ };
40
+ let b = od.borrow();
41
+ match key {
42
+ Value::Symbol(s) => b.symbols.as_ref()?.get(&s.id).cloned(),
43
+ Value::Number(n) => {
44
+ let k: Arc<str> = n.to_string().into();
45
+ b.strings.get(&k).cloned()
46
+ }
47
+ Value::String(k) => b.strings.get(k.as_ref()).cloned(),
48
+ _ => None,
49
+ }
50
+ }
51
+
52
+ pub fn eval_object_set(obj: &Value, key: &Value, val: Value) -> Result<(), String> {
53
+ let Value::Object(od) = obj else {
54
+ return Err("Cannot set property on non-object".to_string());
55
+ };
56
+ let mut b = od.borrow_mut();
57
+ match key {
58
+ Value::Symbol(s) => {
59
+ if b.symbols.is_none() {
60
+ b.symbols = Some(AHashMap::default());
61
+ }
62
+ b.symbols.as_mut().unwrap().insert(s.id, val);
63
+ Ok(())
64
+ }
65
+ Value::Number(n) => {
66
+ b.strings.insert(n.to_string().into(), val);
67
+ Ok(())
68
+ }
69
+ Value::String(k) => {
70
+ b.strings.insert(Arc::clone(k), val);
71
+ Ok(())
72
+ }
73
+ _ => Err("Object key must be string, number, or symbol".to_string()),
74
+ }
75
+ }
76
+
77
+ pub fn eval_object_has(obj: &Value, key: &Value) -> bool {
78
+ let Value::Object(od) = obj else {
79
+ return false;
80
+ };
81
+ let b = od.borrow();
82
+ match key {
83
+ Value::Symbol(s) => b.symbols.as_ref().is_some_and(|m| m.contains_key(&s.id)),
84
+ Value::Number(n) => {
85
+ let k: Arc<str> = n.to_string().into();
86
+ b.strings.contains_key(&k)
87
+ }
88
+ Value::String(k) => b.strings.contains_key(k.as_ref()),
89
+ _ => false,
90
+ }
91
+ }
92
+ use tishlang_core::TishOpaque;
93
+ #[cfg(feature = "http")]
94
+ use tishlang_core::TishPromise;
95
+
96
+ #[cfg(feature = "http")]
97
+ pub use crate::promise::PromiseResolver;
98
+ #[cfg(feature = "regex")]
99
+ pub use crate::regex::TishRegExp;
100
+
101
+ /// Native function type - takes args, returns Result<Value, String>
102
+ pub type NativeFn = fn(&[Value]) -> Result<Value, String>;
103
+
104
+ #[derive(Clone)]
105
+ pub enum Value {
106
+ Number(f64),
107
+ String(Arc<str>),
108
+ Bool(bool),
109
+ Null,
110
+ Array(Rc<RefCell<Vec<Value>>>),
111
+ Object(Rc<RefCell<EvalObjectData>>),
112
+ Symbol(Arc<TishSymbol>),
113
+ /// User-defined function with AST body
114
+ Function {
115
+ formals: Arc<[FunParam]>,
116
+ rest_param: Option<Arc<str>>,
117
+ body: Arc<Statement>,
118
+ },
119
+ /// Native/builtin function
120
+ Native(NativeFn),
121
+ /// HTTP serve function (needs special handling for callbacks)
122
+ #[cfg(feature = "http")]
123
+ Serve,
124
+ #[cfg(feature = "regex")]
125
+ RegExp(Rc<RefCell<TishRegExp>>),
126
+ /// Promise (ECMA-262 §27.2). Requires http feature for tokio.
127
+ #[cfg(feature = "http")]
128
+ Promise(crate::promise::PromiseRef),
129
+ /// Internal: resolve/reject functions passed to executor. Not user-facing.
130
+ #[cfg(feature = "http")]
131
+ PromiseResolver(PromiseResolver),
132
+ /// Promise constructor: Promise(executor). Requires special call handling.
133
+ #[cfg(feature = "http")]
134
+ PromiseConstructor,
135
+ /// Bound promise method: promise.then/catch/finally - captures the promise for the call.
136
+ #[cfg(feature = "http")]
137
+ BoundPromiseMethod(crate::promise::PromiseRef, std::sync::Arc<str>),
138
+ /// Timer builtins: setTimeout, setInterval. Need evaluator for callback.
139
+ #[cfg(feature = "timers")]
140
+ TimerBuiltin(std::sync::Arc<str>),
141
+ /// Native `tishlang_core` Promise (fetch / reader.read / response.text).
142
+ #[cfg(feature = "http")]
143
+ CorePromise(Arc<dyn TishPromise>),
144
+ /// `tishlang_core::Value::Function` (native callbacks, `new` constructors, fetch/ws when enabled).
145
+ CoreFn(CoreNativeFn),
146
+ /// Opaque handle to a native Rust type (e.g. Polars DataFrame).
147
+ Opaque(Arc<dyn TishOpaque>),
148
+ /// Bound method on an opaque value (opaque, method_name). Callable.
149
+ OpaqueMethod(Arc<dyn TishOpaque>, Arc<str>),
150
+ }
151
+
152
+ impl std::fmt::Debug for Value {
153
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154
+ match self {
155
+ Value::Number(n) => write!(f, "Number({})", n),
156
+ Value::String(s) => write!(f, "String({:?})", s.as_ref()),
157
+ Value::Bool(b) => write!(f, "Bool({})", b),
158
+ Value::Null => write!(f, "Null"),
159
+ Value::Array(arr) => write!(f, "Array({:?})", arr.borrow()),
160
+ Value::Object(obj) => write!(f, "Object({:?})", obj.borrow()),
161
+ Value::Function { .. } => write!(f, "Function"),
162
+ Value::Native(_) => write!(f, "Native"),
163
+ #[cfg(feature = "http")]
164
+ Value::Serve => write!(f, "Serve"),
165
+ #[cfg(feature = "regex")]
166
+ Value::RegExp(re) => write!(
167
+ f,
168
+ "RegExp(/{}/{})",
169
+ re.borrow().source,
170
+ re.borrow().flags_string()
171
+ ),
172
+ #[cfg(feature = "http")]
173
+ Value::Promise(_) => write!(f, "Promise"),
174
+ #[cfg(feature = "http")]
175
+ Value::PromiseResolver(_) => write!(f, "[PromiseResolver]"),
176
+ #[cfg(feature = "http")]
177
+ Value::PromiseConstructor => write!(f, "[Function: Promise]"),
178
+ #[cfg(feature = "http")]
179
+ Value::BoundPromiseMethod(_, _) => write!(f, "[Function]"),
180
+ #[cfg(feature = "timers")]
181
+ Value::TimerBuiltin(_) => write!(f, "[Function]"),
182
+ #[cfg(feature = "http")]
183
+ Value::CorePromise(_) => write!(f, "Promise"),
184
+ Value::CoreFn(_) => write!(f, "CoreFn"),
185
+ Value::Opaque(o) => write!(f, "{}(opaque)", o.type_name()),
186
+ Value::OpaqueMethod(_, _) => write!(f, "[Function]"),
187
+ Value::Symbol(s) => write!(f, "Symbol({})", s.id),
188
+ }
189
+ }
190
+ }
191
+
192
+ impl std::fmt::Display for Value {
193
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194
+ match self {
195
+ Value::Number(n) => {
196
+ if n.is_nan() {
197
+ write!(f, "NaN")
198
+ } else if *n == f64::INFINITY {
199
+ write!(f, "Infinity")
200
+ } else if *n == f64::NEG_INFINITY {
201
+ write!(f, "-Infinity")
202
+ } else {
203
+ write!(f, "{}", n)
204
+ }
205
+ }
206
+ Value::String(s) => write!(f, "{}", s),
207
+ Value::Bool(b) => write!(f, "{}", b),
208
+ Value::Null => write!(f, "null"),
209
+ Value::Array(arr) => {
210
+ let inner: Vec<String> = arr.borrow().iter().map(|v| v.to_string()).collect();
211
+ write!(f, "[{}]", inner.join(", "))
212
+ }
213
+ Value::Object(obj) => {
214
+ let inner: Vec<String> = obj
215
+ .borrow()
216
+ .strings
217
+ .iter()
218
+ .map(|(k, v)| format!("{}: {}", k.as_ref(), v))
219
+ .collect();
220
+ write!(f, "{{{}}}", inner.join(", "))
221
+ }
222
+ Value::Symbol(s) => {
223
+ if let Some(d) = &s.description {
224
+ write!(f, "Symbol({})", d)
225
+ } else {
226
+ write!(f, "Symbol()")
227
+ }
228
+ }
229
+ Value::Function { .. } => write!(f, "[Function]"),
230
+ Value::Native(_) => write!(f, "[NativeFunction]"),
231
+ #[cfg(feature = "http")]
232
+ Value::Serve => write!(f, "[NativeFunction: serve]"),
233
+ #[cfg(feature = "regex")]
234
+ Value::RegExp(re) => {
235
+ let re = re.borrow();
236
+ write!(f, "/{}/{}", re.source, re.flags_string())
237
+ }
238
+ #[cfg(feature = "http")]
239
+ Value::Promise(_) => write!(f, "[Promise]"),
240
+ #[cfg(feature = "http")]
241
+ Value::PromiseResolver(_) => write!(f, "[Function]"),
242
+ #[cfg(feature = "http")]
243
+ Value::PromiseConstructor => write!(f, "function Promise() {{ [native code] }}"),
244
+ #[cfg(feature = "http")]
245
+ Value::BoundPromiseMethod(_, _) => write!(f, "[Function]"),
246
+ #[cfg(feature = "timers")]
247
+ Value::TimerBuiltin(_) => write!(f, "[Function]"),
248
+ #[cfg(feature = "http")]
249
+ Value::CorePromise(_) => write!(f, "[Promise]"),
250
+ Value::CoreFn(_) => write!(f, "[Function]"),
251
+ Value::Opaque(o) => write!(f, "[object {}]", o.type_name()),
252
+ Value::OpaqueMethod(_, _) => write!(f, "[Function]"),
253
+ }
254
+ }
255
+ }
256
+
257
+ impl Value {
258
+ pub fn is_truthy(&self) -> bool {
259
+ match self {
260
+ Value::Null => false,
261
+ Value::Bool(b) => *b,
262
+ Value::Number(n) => *n != 0.0 && !n.is_nan(),
263
+ Value::String(s) => !s.is_empty(),
264
+ _ => true,
265
+ }
266
+ }
267
+
268
+ pub fn strict_eq(&self, other: &Value) -> bool {
269
+ match (self, other) {
270
+ (Value::Number(a), Value::Number(b)) => {
271
+ if a.is_nan() || b.is_nan() {
272
+ false
273
+ } else {
274
+ a == b
275
+ }
276
+ }
277
+ (Value::String(a), Value::String(b)) => a == b,
278
+ (Value::Bool(a), Value::Bool(b)) => a == b,
279
+ (Value::Null, Value::Null) => true,
280
+ (Value::Array(a), Value::Array(b)) => Rc::ptr_eq(a, b),
281
+ (Value::Object(a), Value::Object(b)) => Rc::ptr_eq(a, b),
282
+ (Value::Symbol(a), Value::Symbol(b)) => Arc::ptr_eq(a, b),
283
+ (Value::Opaque(a), Value::Opaque(b)) => Arc::ptr_eq(a, b),
284
+ (Value::OpaqueMethod(a, ak), Value::OpaqueMethod(b, bk)) => {
285
+ Arc::ptr_eq(a, b) && ak == bk
286
+ }
287
+ _ => false,
288
+ }
289
+ }
290
+
291
+ /// Create a new array Value from a Vec.
292
+ pub fn array(items: Vec<Value>) -> Self {
293
+ Value::Array(Rc::new(RefCell::new(items)))
294
+ }
295
+
296
+ /// Create a new object Value from a property map.
297
+ pub fn object(map: PropMap) -> Self {
298
+ Value::Object(Rc::new(RefCell::new(EvalObjectData::from_strings(map))))
299
+ }
300
+
301
+ /// Create an empty array Value.
302
+ pub fn empty_array() -> Self {
303
+ Value::Array(Rc::new(RefCell::new(Vec::new())))
304
+ }
305
+
306
+ /// Create an empty object Value.
307
+ pub fn empty_object() -> Self {
308
+ Value::Object(Rc::new(RefCell::new(EvalObjectData::default())))
309
+ }
310
+
311
+ /// Extract the number value, if this is a Number.
312
+ pub fn as_number(&self) -> Option<f64> {
313
+ match self {
314
+ Value::Number(n) => Some(*n),
315
+ _ => None,
316
+ }
317
+ }
318
+ }
@@ -0,0 +1,111 @@
1
+ //! Conversion between tishlang_eval::Value and tishlang_core::Value for opaque method calls.
2
+
3
+ use std::cell::RefCell;
4
+ use std::rc::Rc;
5
+ use std::sync::Arc;
6
+
7
+ #[cfg(feature = "regex")]
8
+ use tishlang_core::TishRegExp;
9
+ use ahash::AHashMap;
10
+ use tishlang_core::{ObjectData, ObjectMap, Value as CoreValue, VmRef};
11
+
12
+ use crate::value::{EvalObjectData, PropMap, Value};
13
+
14
+ /// Convert interpreter Value to core Value. Fails for interpreter-only variants.
15
+ pub fn eval_to_core(v: &Value) -> Result<CoreValue, String> {
16
+ match v {
17
+ Value::Number(n) => Ok(CoreValue::Number(*n)),
18
+ Value::String(s) => Ok(CoreValue::String(Arc::clone(s))),
19
+ Value::Bool(b) => Ok(CoreValue::Bool(*b)),
20
+ Value::Null => Ok(CoreValue::Null),
21
+ Value::Array(arr) => {
22
+ let mut out = Vec::new();
23
+ for item in arr.borrow().iter() {
24
+ out.push(eval_to_core(item)?);
25
+ }
26
+ Ok(CoreValue::Array(VmRef::new(out)))
27
+ }
28
+ Value::Object(map) => {
29
+ let b = map.borrow();
30
+ let mut strings = ObjectMap::default();
31
+ for (k, v) in b.strings.iter() {
32
+ strings.insert(Arc::clone(k), eval_to_core(v)?);
33
+ }
34
+ let symbols = if let Some(ss) = &b.symbols {
35
+ let mut sm = AHashMap::default();
36
+ for (id, v) in ss.iter() {
37
+ sm.insert(*id, eval_to_core(v)?);
38
+ }
39
+ Some(sm)
40
+ } else {
41
+ None
42
+ };
43
+ Ok(CoreValue::Object(VmRef::new(ObjectData { strings, symbols })))
44
+ }
45
+ Value::Symbol(s) => Ok(CoreValue::Symbol(Arc::clone(s))),
46
+ Value::Opaque(o) => Ok(CoreValue::Opaque(Arc::clone(o))),
47
+ _ => Err(format!(
48
+ "Cannot pass {:?} to native function (unsupported type)",
49
+ std::mem::discriminant(v)
50
+ )),
51
+ }
52
+ }
53
+
54
+ /// Convert core Value to interpreter Value.
55
+ pub fn core_to_eval(v: CoreValue) -> Value {
56
+ match v {
57
+ CoreValue::Number(n) => Value::Number(n),
58
+ CoreValue::String(s) => Value::String(s),
59
+ CoreValue::Bool(b) => Value::Bool(b),
60
+ CoreValue::Null => Value::Null,
61
+ CoreValue::Array(arr) => {
62
+ let mut out = Vec::new();
63
+ for item in arr.borrow().iter() {
64
+ out.push(core_to_eval(item.clone()));
65
+ }
66
+ Value::Array(Rc::new(RefCell::new(out)))
67
+ }
68
+ CoreValue::Object(map) => {
69
+ let b = map.borrow();
70
+ let mut out = PropMap::default();
71
+ for (k, v) in b.strings.iter() {
72
+ out.insert(Arc::clone(k), core_to_eval(v.clone()));
73
+ }
74
+ let mut eod = EvalObjectData::from_strings(out);
75
+ if let Some(ss) = &b.symbols {
76
+ let mut es = AHashMap::default();
77
+ for (id, v) in ss.iter() {
78
+ es.insert(*id, core_to_eval(v.clone()));
79
+ }
80
+ eod.symbols = Some(es);
81
+ }
82
+ Value::Object(Rc::new(RefCell::new(eod)))
83
+ }
84
+ CoreValue::Symbol(s) => Value::Symbol(Arc::clone(&s)),
85
+ CoreValue::Opaque(o) => Value::Opaque(o),
86
+ #[cfg(feature = "http")]
87
+ CoreValue::Promise(p) => Value::CorePromise(Arc::clone(&p)),
88
+ #[cfg(not(feature = "http"))]
89
+ CoreValue::Promise(_) => Value::Null,
90
+ // `CoreNativeFn` is feature-gated (Rc vs Arc), so use Clone::clone
91
+ // which works for either.
92
+ CoreValue::Function(f) => Value::CoreFn(f.clone()),
93
+ // tishlang_core gets regex from http or regex features; handle RegExp when it exists
94
+ #[cfg(any(feature = "http", feature = "regex"))]
95
+ CoreValue::RegExp(re) => {
96
+ #[cfg(feature = "regex")]
97
+ {
98
+ // Core uses `VmRef<TishRegExp>` (potentially `Arc<Mutex>`),
99
+ // interpreter uses `Rc<RefCell<TishRegExp>>`. Clone the
100
+ // inner state across so the two storage shapes can coexist.
101
+ let inner: TishRegExp = re.borrow().clone();
102
+ Value::RegExp(Rc::new(RefCell::new(inner)))
103
+ }
104
+ #[cfg(not(feature = "regex"))]
105
+ {
106
+ let _ = re;
107
+ Value::Null
108
+ }
109
+ }
110
+ }
111
+ }
@@ -0,0 +1,16 @@
1
+ [package]
2
+ name = "tishlang_fmt"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ description = "Opinionated formatter for Tish source (parse → pretty-print)"
6
+ license-file = { workspace = true }
7
+ repository = { workspace = true }
8
+
9
+ [[bin]]
10
+ name = "tish-fmt"
11
+ path = "src/bin/tish-fmt.rs"
12
+
13
+ [dependencies]
14
+ clap = { version = "4.6.0", features = ["derive"] }
15
+ tishlang_ast = { path = "../tish_ast", version = ">=0.1" }
16
+ tishlang_parser = { path = "../tish_parser", version = ">=0.1" }
@@ -0,0 +1,41 @@
1
+ //! Standalone formatter — not part of the `tish` compiler CLI.
2
+
3
+ use std::fs;
4
+
5
+ use clap::Parser;
6
+
7
+ #[derive(Parser)]
8
+ #[command(name = "tish-fmt")]
9
+ #[command(about = "Format Tish source (pretty-print via AST)")]
10
+ struct Cli {
11
+ #[arg(required = true)]
12
+ file: String,
13
+ #[arg(long)]
14
+ check: bool,
15
+ }
16
+
17
+ fn main() {
18
+ let cli = Cli::parse();
19
+ if let Err(e) = run(&cli.file, cli.check) {
20
+ eprintln!("{}", e);
21
+ std::process::exit(1);
22
+ }
23
+ }
24
+
25
+ fn run(path: &str, check: bool) -> Result<(), String> {
26
+ let source = fs::read_to_string(path).map_err(|e| format!("Cannot read {}: {}", path, e))?;
27
+ let formatted = tishlang_fmt::format_source(&source)?;
28
+ if check {
29
+ if formatted != source {
30
+ return Err(format!(
31
+ "Format check failed: {} needs formatting (run `tish-fmt {}`)",
32
+ path, path
33
+ ));
34
+ }
35
+ println!("{}: OK", path);
36
+ return Ok(());
37
+ }
38
+ fs::write(path, formatted).map_err(|e| format!("Cannot write {}: {}", path, e))?;
39
+ println!("Formatted: {}", path);
40
+ Ok(())
41
+ }