@tishlang/tish-format 1.0.12 → 2.0.1

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 (189) hide show
  1. package/Cargo.toml +51 -0
  2. package/LICENSE +13 -0
  3. package/bin/tish-format +0 -0
  4. package/crates/js_to_tish/Cargo.toml +11 -0
  5. package/crates/js_to_tish/README.md +18 -0
  6. package/crates/js_to_tish/src/error.rs +55 -0
  7. package/crates/js_to_tish/src/lib.rs +11 -0
  8. package/crates/js_to_tish/src/span_util.rs +35 -0
  9. package/crates/js_to_tish/src/transform/expr.rs +611 -0
  10. package/crates/js_to_tish/src/transform/stmt.rs +503 -0
  11. package/crates/js_to_tish/src/transform.rs +60 -0
  12. package/crates/tish/Cargo.toml +62 -0
  13. package/crates/tish/build.rs +21 -0
  14. package/crates/tish/src/cargo_native_registry.rs +32 -0
  15. package/crates/tish/src/cli_help.rs +576 -0
  16. package/crates/tish/src/main.rs +853 -0
  17. package/crates/tish/src/repl_completion.rs +199 -0
  18. package/crates/tish/tests/cargo_example_compile.rs +67 -0
  19. package/crates/tish/tests/error_source_location.rs +36 -0
  20. package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
  21. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
  22. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
  23. package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
  24. package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
  25. package/crates/tish/tests/fixtures/runtime_error_location.tish +5 -0
  26. package/crates/tish/tests/fixtures/trycatch_runtime_errors.tish +15 -0
  27. package/crates/tish/tests/fixtures/tty_capability.tish +9 -0
  28. package/crates/tish/tests/integration_test.rs +1406 -0
  29. package/crates/tish/tests/run_optimize_stdout_parity.rs +50 -0
  30. package/crates/tish/tests/shortcircuit.rs +65 -0
  31. package/crates/tish/tests/trycatch_runtime_errors.rs +45 -0
  32. package/crates/tish/tests/tty_capability.rs +43 -0
  33. package/crates/tish_ast/Cargo.toml +9 -0
  34. package/crates/tish_ast/src/ast.rs +649 -0
  35. package/crates/tish_ast/src/lib.rs +5 -0
  36. package/crates/tish_build_utils/Cargo.toml +11 -0
  37. package/crates/tish_build_utils/src/lib.rs +577 -0
  38. package/crates/tish_builtins/Cargo.toml +22 -0
  39. package/crates/tish_builtins/src/array.rs +803 -0
  40. package/crates/tish_builtins/src/collections.rs +481 -0
  41. package/crates/tish_builtins/src/construct.rs +199 -0
  42. package/crates/tish_builtins/src/date.rs +538 -0
  43. package/crates/tish_builtins/src/globals.rs +293 -0
  44. package/crates/tish_builtins/src/helpers.rs +35 -0
  45. package/crates/tish_builtins/src/iterator.rs +129 -0
  46. package/crates/tish_builtins/src/lib.rs +21 -0
  47. package/crates/tish_builtins/src/math.rs +89 -0
  48. package/crates/tish_builtins/src/number.rs +96 -0
  49. package/crates/tish_builtins/src/object.rs +36 -0
  50. package/crates/tish_builtins/src/string.rs +646 -0
  51. package/crates/tish_builtins/src/symbol.rs +83 -0
  52. package/crates/tish_builtins/src/typedarrays.rs +298 -0
  53. package/crates/tish_bytecode/Cargo.toml +17 -0
  54. package/crates/tish_bytecode/src/chunk.rs +164 -0
  55. package/crates/tish_bytecode/src/compiler.rs +2604 -0
  56. package/crates/tish_bytecode/src/encoding.rs +102 -0
  57. package/crates/tish_bytecode/src/lib.rs +20 -0
  58. package/crates/tish_bytecode/src/opcode.rs +185 -0
  59. package/crates/tish_bytecode/src/peephole.rs +189 -0
  60. package/crates/tish_bytecode/src/serialize.rs +193 -0
  61. package/crates/tish_bytecode/tests/break_continue_bytecode.rs +44 -0
  62. package/crates/tish_bytecode/tests/constant_folding.rs +84 -0
  63. package/crates/tish_bytecode/tests/sort_optimization.rs +31 -0
  64. package/crates/tish_compile/Cargo.toml +27 -0
  65. package/crates/tish_compile/src/check.rs +774 -0
  66. package/crates/tish_compile/src/codegen.rs +7317 -0
  67. package/crates/tish_compile/src/infer.rs +1681 -0
  68. package/crates/tish_compile/src/lib.rs +206 -0
  69. package/crates/tish_compile/src/resolve.rs +1951 -0
  70. package/crates/tish_compile/src/types.rs +605 -0
  71. package/crates/tish_compile_js/Cargo.toml +18 -0
  72. package/crates/tish_compile_js/examples/jsx_vdom_smoke.tish +8 -0
  73. package/crates/tish_compile_js/src/codegen.rs +938 -0
  74. package/crates/tish_compile_js/src/error.rs +20 -0
  75. package/crates/tish_compile_js/src/lib.rs +26 -0
  76. package/crates/tish_compile_js/src/tests_jsx.rs +414 -0
  77. package/crates/tish_compiler_wasm/Cargo.toml +21 -0
  78. package/crates/tish_compiler_wasm/src/lib.rs +57 -0
  79. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +473 -0
  80. package/crates/tish_core/Cargo.toml +32 -0
  81. package/crates/tish_core/src/console_style.rs +170 -0
  82. package/crates/tish_core/src/json.rs +430 -0
  83. package/crates/tish_core/src/lib.rs +20 -0
  84. package/crates/tish_core/src/macros.rs +36 -0
  85. package/crates/tish_core/src/shape.rs +85 -0
  86. package/crates/tish_core/src/uri.rs +118 -0
  87. package/crates/tish_core/src/value.rs +1350 -0
  88. package/crates/tish_core/src/vmref.rs +183 -0
  89. package/crates/tish_cranelift/Cargo.toml +19 -0
  90. package/crates/tish_cranelift/src/lib.rs +43 -0
  91. package/crates/tish_cranelift/src/link.rs +130 -0
  92. package/crates/tish_cranelift/src/lower.rs +85 -0
  93. package/crates/tish_cranelift_runtime/Cargo.toml +26 -0
  94. package/crates/tish_cranelift_runtime/src/lib.rs +45 -0
  95. package/crates/tish_eval/Cargo.toml +51 -0
  96. package/crates/tish_eval/src/eval.rs +4265 -0
  97. package/crates/tish_eval/src/http.rs +191 -0
  98. package/crates/tish_eval/src/lib.rs +99 -0
  99. package/crates/tish_eval/src/natives.rs +551 -0
  100. package/crates/tish_eval/src/promise.rs +179 -0
  101. package/crates/tish_eval/src/regex.rs +299 -0
  102. package/crates/tish_eval/src/timers.rs +120 -0
  103. package/crates/tish_eval/src/value.rs +336 -0
  104. package/crates/tish_eval/src/value_convert.rs +117 -0
  105. package/crates/tish_ffi/Cargo.toml +26 -0
  106. package/crates/tish_ffi/src/lib.rs +518 -0
  107. package/crates/tish_ffi/tests/fixtures/testmod/Cargo.toml +18 -0
  108. package/crates/tish_ffi/tests/fixtures/testmod/src/lib.rs +46 -0
  109. package/crates/tish_ffi/tests/loader.rs +65 -0
  110. package/crates/tish_fmt/Cargo.toml +16 -0
  111. package/crates/tish_fmt/src/bin/tish-fmt.rs +41 -0
  112. package/crates/tish_fmt/src/lib.rs +2157 -0
  113. package/crates/tish_jsx_web/Cargo.toml +9 -0
  114. package/crates/tish_jsx_web/README.md +5 -0
  115. package/crates/tish_jsx_web/src/lib.rs +2 -0
  116. package/crates/tish_lexer/Cargo.toml +9 -0
  117. package/crates/tish_lexer/src/lib.rs +1104 -0
  118. package/crates/tish_lexer/src/token.rs +170 -0
  119. package/crates/tish_lint/Cargo.toml +18 -0
  120. package/crates/tish_lint/src/bin/tish-lint.rs +195 -0
  121. package/crates/tish_lint/src/lib.rs +281 -0
  122. package/crates/tish_llvm/Cargo.toml +13 -0
  123. package/crates/tish_llvm/src/lib.rs +115 -0
  124. package/crates/tish_lsp/Cargo.toml +25 -0
  125. package/crates/tish_lsp/README.md +26 -0
  126. package/crates/tish_lsp/src/builtin_goto.rs +362 -0
  127. package/crates/tish_lsp/src/import_goto.rs +564 -0
  128. package/crates/tish_lsp/src/main.rs +1459 -0
  129. package/crates/tish_native/Cargo.toml +16 -0
  130. package/crates/tish_native/src/build.rs +481 -0
  131. package/crates/tish_native/src/config.rs +48 -0
  132. package/crates/tish_native/src/lib.rs +416 -0
  133. package/crates/tish_opt/Cargo.toml +13 -0
  134. package/crates/tish_opt/src/lib.rs +1046 -0
  135. package/crates/tish_parser/Cargo.toml +11 -0
  136. package/crates/tish_parser/src/lib.rs +386 -0
  137. package/crates/tish_parser/src/parser.rs +2726 -0
  138. package/crates/tish_pg/Cargo.toml +34 -0
  139. package/crates/tish_pg/README.md +38 -0
  140. package/crates/tish_pg/src/error.rs +52 -0
  141. package/crates/tish_pg/src/lib.rs +955 -0
  142. package/crates/tish_resolve/Cargo.toml +13 -0
  143. package/crates/tish_resolve/src/lib.rs +3601 -0
  144. package/crates/tish_resolve/src/pos.rs +141 -0
  145. package/crates/tish_runtime/Cargo.toml +100 -0
  146. package/crates/tish_runtime/src/http.rs +1347 -0
  147. package/crates/tish_runtime/src/http_fetch.rs +492 -0
  148. package/crates/tish_runtime/src/http_hyper.rs +441 -0
  149. package/crates/tish_runtime/src/http_prefork.rs +189 -0
  150. package/crates/tish_runtime/src/lib.rs +1447 -0
  151. package/crates/tish_runtime/src/native_promise.rs +15 -0
  152. package/crates/tish_runtime/src/promise.rs +558 -0
  153. package/crates/tish_runtime/src/promise_io.rs +38 -0
  154. package/crates/tish_runtime/src/timers.rs +172 -0
  155. package/crates/tish_runtime/src/tty.rs +226 -0
  156. package/crates/tish_runtime/src/ws.rs +778 -0
  157. package/crates/tish_runtime/tests/fetch_readable_stream.rs +102 -0
  158. package/crates/tish_ui/Cargo.toml +17 -0
  159. package/crates/tish_ui/src/jsx.rs +692 -0
  160. package/crates/tish_ui/src/lib.rs +20 -0
  161. package/crates/tish_ui/src/runtime/hooks.rs +573 -0
  162. package/crates/tish_ui/src/runtime/mod.rs +183 -0
  163. package/crates/tish_vm/Cargo.toml +60 -0
  164. package/crates/tish_vm/src/jit.rs +1050 -0
  165. package/crates/tish_vm/src/lib.rs +41 -0
  166. package/crates/tish_vm/src/vm.rs +3536 -0
  167. package/crates/tish_vm/tests/concurrent_shared_state.rs +140 -0
  168. package/crates/tish_vm/tests/fixtures/or_string_cmd.tish +2 -0
  169. package/crates/tish_vm/tests/lexical_scope_declare.rs +34 -0
  170. package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +150 -0
  171. package/crates/tish_wasm/Cargo.toml +15 -0
  172. package/crates/tish_wasm/src/lib.rs +428 -0
  173. package/crates/tish_wasm_runtime/Cargo.toml +37 -0
  174. package/crates/tish_wasm_runtime/src/gpu.rs +429 -0
  175. package/crates/tish_wasm_runtime/src/lib.rs +42 -0
  176. package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
  177. package/crates/tishlang_cargo_bindgen/src/classify.rs +261 -0
  178. package/crates/tishlang_cargo_bindgen/src/discover.rs +125 -0
  179. package/crates/tishlang_cargo_bindgen/src/infer.rs +382 -0
  180. package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
  181. package/crates/tishlang_cargo_bindgen/src/main.rs +167 -0
  182. package/crates/tishlang_cargo_bindgen/src/metadata.rs +117 -0
  183. package/justfile +276 -0
  184. package/package.json +2 -2
  185. package/platform/darwin-arm64/tish-fmt +0 -0
  186. package/platform/darwin-x64/tish-fmt +0 -0
  187. package/platform/linux-arm64/tish-fmt +0 -0
  188. package/platform/linux-x64/tish-fmt +0 -0
  189. package/platform/win32-x64/tish-fmt.exe +0 -0
@@ -0,0 +1,199 @@
1
+ //! REPL tab completion: dotted (e.g. `a.` -> properties/methods) and bare-word (e.g. `con` -> console).
2
+ //! Grey preview hint below the line (like Node) and Tab for full list.
3
+
4
+ use std::borrow::Cow;
5
+
6
+ use tishlang_core::VmRef;
7
+
8
+ use rustyline::completion::{Completer, Pair};
9
+ use rustyline::highlight::Highlighter;
10
+ use rustyline::hint::Hinter;
11
+ use rustyline::validate::Validator;
12
+ use rustyline::Context;
13
+ use rustyline::Helper;
14
+
15
+ use tishlang_bytecode::{compile_for_repl, compile_for_repl_unoptimized};
16
+ use tishlang_vm::Vm;
17
+
18
+ /// Tish keywords for bare-word completion (Python-style).
19
+ const KEYWORDS: &[&str] = &[
20
+ "async", "await", "break", "case", "catch", "const", "continue", "default", "do", "else",
21
+ "export", "false", "finally", "for", "fn", "function", "if", "import", "in", "let", "null",
22
+ "of", "return", "switch", "throw", "true", "try", "typeof", "void", "while",
23
+ ];
24
+
25
+ /// ANSI dim (grey) for hint preview; reset after.
26
+ const ANSI_DIM: &str = "\x1b[2m";
27
+ const ANSI_RESET: &str = "\x1b[0m";
28
+
29
+ /// Tab completer that evaluates the expression before the last `.` and suggests property/method names.
30
+ pub struct ReplCompleter {
31
+ pub vm: VmRef<Vm>,
32
+ pub no_optimize: bool,
33
+ }
34
+
35
+ impl ReplCompleter {
36
+ /// Find the start of the identifier (or keyword) being typed before the cursor.
37
+ /// Returns (byte_start, prefix_string).
38
+ fn ident_prefix_at_cursor<'a>(&self, line_before_cursor: &'a str) -> (usize, &'a str) {
39
+ let s = line_before_cursor;
40
+ if s.is_empty() {
41
+ return (0, "");
42
+ }
43
+ let mut start = s.len();
44
+ for (i, c) in s.char_indices().rev() {
45
+ if c.is_alphanumeric() || c == '_' {
46
+ start = i;
47
+ } else {
48
+ break;
49
+ }
50
+ }
51
+ (start, &s[start..])
52
+ }
53
+
54
+ /// Bare-word completions: globals + keywords that start with prefix (Python-style).
55
+ /// When nothing is typed (empty prefix), return no completions so we don't show a hint at the prompt.
56
+ fn get_bare_completions(&self, line_before_cursor: &str) -> (usize, Vec<String>) {
57
+ let (start, prefix) = self.ident_prefix_at_cursor(line_before_cursor);
58
+ if prefix.is_empty() {
59
+ return (start, vec![]);
60
+ }
61
+ let mut names: Vec<String> = self.vm.borrow().global_names();
62
+ names.extend(KEYWORDS.iter().map(|s| (*s).to_string()));
63
+ names.sort();
64
+ names.dedup();
65
+ let filtered: Vec<String> = names
66
+ .into_iter()
67
+ .filter(|k| k.starts_with(prefix))
68
+ .collect();
69
+ (start, filtered)
70
+ }
71
+
72
+ /// Get completions for dotted expr (e.g. `a.` -> member names). Returns (start_offset, list).
73
+ fn get_dotted_completions(&self, line_before_cursor: &str) -> Option<(usize, Vec<String>)> {
74
+ let last_dot = line_before_cursor.rfind('.')?;
75
+ let prefix_expr = line_before_cursor[..last_dot].trim();
76
+ if prefix_expr.is_empty() {
77
+ return None;
78
+ }
79
+ let member_prefix = line_before_cursor[last_dot + 1..].trim();
80
+
81
+ let program = tishlang_parser::parse(prefix_expr).ok()?;
82
+ let compile_fn = if self.no_optimize {
83
+ compile_for_repl_unoptimized
84
+ } else {
85
+ compile_for_repl
86
+ };
87
+ let chunk = compile_fn(&program).ok()?;
88
+ let value = self.vm.borrow_mut().run_with_options(&chunk, true).ok()?;
89
+
90
+ let keys = value.completion_keys();
91
+ let filtered: Vec<String> = keys
92
+ .into_iter()
93
+ .filter(|k| k.starts_with(member_prefix))
94
+ .collect();
95
+ Some((last_dot + 1, filtered))
96
+ }
97
+
98
+ /// Unified: dotted if we have a dot and it works, else bare-word.
99
+ fn get_completions(&self, line_before_cursor: &str) -> (usize, Vec<String>) {
100
+ if line_before_cursor.contains('.') {
101
+ if let Some((start, filtered)) = self.get_dotted_completions(line_before_cursor) {
102
+ if !filtered.is_empty() || line_before_cursor.trim_end().ends_with('.') {
103
+ return (start, filtered);
104
+ }
105
+ }
106
+ }
107
+ self.get_bare_completions(line_before_cursor)
108
+ }
109
+
110
+ /// Longest common prefix of a list of strings.
111
+ fn longest_common_prefix(items: &[String]) -> Option<String> {
112
+ let first = items.first()?;
113
+ let mut len = first.len();
114
+ for item in items.iter().skip(1) {
115
+ len = first
116
+ .bytes()
117
+ .zip(item.bytes())
118
+ .take_while(|(a, b)| a == b)
119
+ .count()
120
+ .min(len);
121
+ }
122
+ if len == 0 {
123
+ None
124
+ } else {
125
+ Some(first[..len].to_string())
126
+ }
127
+ }
128
+ }
129
+
130
+ impl Completer for ReplCompleter {
131
+ type Candidate = Pair;
132
+
133
+ fn complete(
134
+ &self,
135
+ line: &str,
136
+ pos: usize,
137
+ _ctx: &Context<'_>,
138
+ ) -> rustyline::Result<(usize, Vec<Pair>)> {
139
+ let line_before_cursor = &line[..pos];
140
+ let (start, filtered) = self.get_completions(line_before_cursor);
141
+ let pairs: Vec<Pair> = filtered
142
+ .into_iter()
143
+ .map(|k| Pair {
144
+ display: k.clone(),
145
+ replacement: k,
146
+ })
147
+ .collect();
148
+ Ok((start, pairs))
149
+ }
150
+ }
151
+
152
+ impl Hinter for ReplCompleter {
153
+ type Hint = String;
154
+
155
+ /// Grey preview: show first completion or common prefix below the line (Node-style).
156
+ fn hint(&self, line: &str, pos: usize, _ctx: &Context<'_>) -> Option<Self::Hint> {
157
+ let line_before_cursor = &line[..pos];
158
+ let (start, filtered) = self.get_completions(line_before_cursor);
159
+ if filtered.is_empty() {
160
+ return None;
161
+ }
162
+ let member_prefix = line_before_cursor.get(start..).unwrap_or("").trim();
163
+ // Hint = text to show after cursor (grey). Single match: full name; multiple: common prefix, or first suggestion.
164
+ let hint = if filtered.len() == 1 {
165
+ filtered[0].clone()
166
+ } else if let Some(lcp) = Self::longest_common_prefix(&filtered) {
167
+ if lcp.len() > member_prefix.len() {
168
+ lcp
169
+ } else {
170
+ // No useful common prefix (e.g. "a." -> many methods); show first as preview like Node.
171
+ filtered[0].clone()
172
+ }
173
+ } else {
174
+ filtered[0].clone()
175
+ };
176
+ // Only show the part not yet typed.
177
+ if hint.starts_with(member_prefix) && hint.len() > member_prefix.len() {
178
+ Some(hint[member_prefix.len()..].to_string())
179
+ } else if hint == member_prefix {
180
+ None
181
+ } else {
182
+ Some(hint)
183
+ }
184
+ }
185
+ }
186
+
187
+ impl Highlighter for ReplCompleter {
188
+ /// Show hint in dim grey (Node-style preview).
189
+ fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
190
+ if hint.is_empty() {
191
+ return Cow::Borrowed(hint);
192
+ }
193
+ Cow::Owned(format!("{ANSI_DIM}{hint}{ANSI_RESET}"))
194
+ }
195
+ }
196
+
197
+ impl Validator for ReplCompleter {}
198
+
199
+ impl Helper for ReplCompleter {}
@@ -0,0 +1,67 @@
1
+ //! `cargo:` + `tish.rustDependencies` using the in-repo fixture at `tests/fixtures/cargo_example_project/`
2
+ //! (same layout as the standalone `tish-cargo-example` template).
3
+
4
+ use std::path::PathBuf;
5
+
6
+ use tishlang_ast::Statement;
7
+ use tishlang_compile::{compile_project_full, merge_modules, resolve_project};
8
+
9
+ fn native_build_features_from_cli(cli_features: &[String]) -> Vec<String> {
10
+ if cli_features.is_empty() {
11
+ let mut v: Vec<String> = tishlang_vm::all_compiled_capabilities()
12
+ .into_iter()
13
+ .collect();
14
+ v.sort();
15
+ v
16
+ } else {
17
+ cli_features.to_vec()
18
+ }
19
+ }
20
+
21
+ #[test]
22
+ fn resolve_and_merge_cargo_example_fixture() {
23
+ let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
24
+ let input_path = manifest_dir
25
+ .join("tests/fixtures/cargo_example_project/src/main.tish")
26
+ .canonicalize()
27
+ .expect("cargo_example_project test fixture");
28
+ let project_root = input_path.parent().map(|p| {
29
+ if p.file_name().and_then(|n| n.to_str()) == Some("src") {
30
+ p.parent().unwrap_or(p)
31
+ } else {
32
+ p
33
+ }
34
+ });
35
+ let modules = resolve_project(&input_path, project_root).unwrap();
36
+ assert_eq!(modules.len(), 1, "expected single entry module");
37
+ let first = &modules[0].program.statements[0];
38
+ let Statement::Import { from, .. } = first else {
39
+ panic!("expected import, got {:?}", first);
40
+ };
41
+ assert_eq!(from.as_ref(), "cargo:demo_shim");
42
+ let _ = merge_modules(modules).unwrap();
43
+ }
44
+
45
+ #[test]
46
+ fn compile_project_full_cargo_example_fixture() {
47
+ let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
48
+ let example_main = manifest_dir
49
+ .join("tests/fixtures/cargo_example_project/src/main.tish")
50
+ .canonicalize()
51
+ .expect("cargo_example_project test fixture");
52
+ let input_path = example_main;
53
+ let project_root = input_path.parent().map(|p| {
54
+ if p.file_name().and_then(|n| n.to_str()) == Some("src") {
55
+ p.parent().unwrap_or(p)
56
+ } else {
57
+ p
58
+ }
59
+ });
60
+ let features = native_build_features_from_cli(&[]);
61
+ let r = compile_project_full(&input_path, project_root, &features, true);
62
+ assert!(
63
+ r.is_ok(),
64
+ "compile_project_full failed: {:?}",
65
+ r.map_err(|e| e.message)
66
+ );
67
+ }
@@ -0,0 +1,36 @@
1
+ //! Issue #74: a runtime error from the bytecode VM carries its source location (`file:line`)
2
+ //! instead of a bare message, so embedders/users can find where it came from.
3
+
4
+ use std::path::PathBuf;
5
+ use std::process::Command;
6
+
7
+ #[test]
8
+ fn runtime_error_reports_source_file_and_line() {
9
+ let tish = PathBuf::from(env!("CARGO_BIN_EXE_tish"));
10
+ let fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
11
+ .join("tests")
12
+ .join("fixtures")
13
+ .join("runtime_error_location.tish");
14
+ assert!(fixture.is_file(), "missing fixture {}", fixture.display());
15
+
16
+ // `obj.field.deep` reads `.field` of `null` on line 4 of the fixture.
17
+ let out = Command::new(&tish)
18
+ .args(["run", "--backend", "vm", fixture.to_str().unwrap()])
19
+ .output()
20
+ .expect("spawn tish run");
21
+ assert!(!out.status.success(), "expected a runtime error");
22
+ let stderr = String::from_utf8_lossy(&out.stderr);
23
+
24
+ assert!(
25
+ stderr.contains(":4"),
26
+ "error should report the source line (4):\n{stderr}"
27
+ );
28
+ assert!(
29
+ stderr.contains("runtime_error_location.tish"),
30
+ "error should report the source file:\n{stderr}"
31
+ );
32
+ assert!(
33
+ stderr.contains("Cannot read property 'field' of null"),
34
+ "error should keep the original message:\n{stderr}"
35
+ );
36
+ }
@@ -0,0 +1,3 @@
1
+ [workspace]
2
+ resolver = "2"
3
+ members = ["crates/demo-shim"]
@@ -0,0 +1,11 @@
1
+ [package]
2
+ name = "demo_shim"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ [lib]
7
+ crate-type = ["rlib"]
8
+
9
+ [dependencies]
10
+ # Fixture lives at crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim → repo crates/tish_core
11
+ tishlang_core = { path = "../../../../../../../crates/tish_core" }
@@ -0,0 +1,12 @@
1
+ //! Rust exports for `import { … } from 'cargo:demo_shim'` (test fixture).
2
+
3
+ use std::sync::Arc;
4
+ use tishlang_core::Value;
5
+
6
+ pub fn greet(args: &[Value]) -> Value {
7
+ let name = match args.first() {
8
+ Some(Value::String(s)) => s.as_ref(),
9
+ _ => "world",
10
+ };
11
+ Value::String(Arc::from(format!("Hello, {}!", name)))
12
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "tish-cargo-example-fixture",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "tish": {
6
+ "rustDependencies": {
7
+ "demo_shim": { "path": "./crates/demo-shim" }
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,3 @@
1
+ import { greet } from 'cargo:demo_shim'
2
+
3
+ console.log(greet('from cargo'))
@@ -0,0 +1,5 @@
1
+ let a = 1
2
+ let b = 2
3
+ let obj = null
4
+ let x = obj.field.deep
5
+ console.log(x)
@@ -0,0 +1,15 @@
1
+ // try/catch catches runtime errors (null property/method access, "not a function")
2
+ // AND throws that cross function-call frames, with the thrown value preserved (issue #60).
3
+ try { let x = null; x.foo() } catch (e) { console.log("A caught") }
4
+ function f() { throw new Error("boom") }
5
+ try { f() } catch (e) { console.log("B caught:", e.message) }
6
+ function g() { let y = null; y.baz() }
7
+ try { g() } catch (e) { console.log("C caught") }
8
+ console.log("D done")
9
+ try { try { throw "inner" } catch (e) { throw "rethrown" } } catch (e) { console.log("E caught:", e) }
10
+ try { console.log("F ok") } catch (e) { console.log("F should not happen") }
11
+ let total = 0
12
+ function risky(n) { if (n > 3) { throw n }; return n }
13
+ let i = 0
14
+ while (i < 6) { try { total = total + risky(i) } catch (e) { total = total + 100 }; i = i + 1 }
15
+ console.log("G total:", total)
@@ -0,0 +1,9 @@
1
+ // tish:tty capability (issue #101). Asserts the module loads and degrades gracefully when
2
+ // stdout/stdin is not a terminal (CI), so the output is deterministic without a real TTY.
3
+ import { isTTY, size, setRawMode, enterAltScreen, leaveAltScreen, read, readLine } from 'tish:tty'
4
+ console.log("isTTY:", typeof isTTY())
5
+ console.log("size:", size() ? "object" : "null")
6
+ console.log("setRawMode:", typeof setRawMode)
7
+ console.log("read:", typeof read)
8
+ console.log("readLine:", typeof readLine)
9
+ console.log("alt:", typeof enterAltScreen, typeof leaveAltScreen)