@tishlang/tish 1.0.7 → 1.0.10

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 (127) hide show
  1. package/Cargo.toml +43 -0
  2. package/LICENSE +13 -0
  3. package/README.md +66 -0
  4. package/crates/js_to_tish/Cargo.toml +9 -0
  5. package/crates/js_to_tish/README.md +18 -0
  6. package/crates/js_to_tish/src/error.rs +61 -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 +608 -0
  10. package/crates/js_to_tish/src/transform/stmt.rs +474 -0
  11. package/crates/js_to_tish/src/transform.rs +60 -0
  12. package/crates/tish/Cargo.toml +44 -0
  13. package/crates/tish/src/main.rs +585 -0
  14. package/crates/tish/src/repl_completion.rs +200 -0
  15. package/crates/tish/tests/integration_test.rs +726 -0
  16. package/crates/tish_ast/Cargo.toml +7 -0
  17. package/crates/tish_ast/src/ast.rs +494 -0
  18. package/crates/tish_ast/src/lib.rs +5 -0
  19. package/crates/tish_build_utils/Cargo.toml +5 -0
  20. package/crates/tish_build_utils/src/lib.rs +175 -0
  21. package/crates/tish_builtins/Cargo.toml +12 -0
  22. package/crates/tish_builtins/src/array.rs +410 -0
  23. package/crates/tish_builtins/src/globals.rs +197 -0
  24. package/crates/tish_builtins/src/helpers.rs +38 -0
  25. package/crates/tish_builtins/src/lib.rs +14 -0
  26. package/crates/tish_builtins/src/math.rs +80 -0
  27. package/crates/tish_builtins/src/object.rs +36 -0
  28. package/crates/tish_builtins/src/string.rs +253 -0
  29. package/crates/tish_bytecode/Cargo.toml +15 -0
  30. package/crates/tish_bytecode/src/chunk.rs +97 -0
  31. package/crates/tish_bytecode/src/compiler.rs +1361 -0
  32. package/crates/tish_bytecode/src/encoding.rs +100 -0
  33. package/crates/tish_bytecode/src/lib.rs +19 -0
  34. package/crates/tish_bytecode/src/opcode.rs +110 -0
  35. package/crates/tish_bytecode/src/peephole.rs +159 -0
  36. package/crates/tish_bytecode/src/serialize.rs +163 -0
  37. package/crates/tish_bytecode/tests/constant_folding.rs +84 -0
  38. package/crates/tish_bytecode/tests/shortcircuit.rs +49 -0
  39. package/crates/tish_bytecode/tests/sort_optimization.rs +31 -0
  40. package/crates/tish_compile/Cargo.toml +21 -0
  41. package/crates/tish_compile/src/codegen.rs +3316 -0
  42. package/crates/tish_compile/src/lib.rs +71 -0
  43. package/crates/tish_compile/src/resolve.rs +631 -0
  44. package/crates/tish_compile/src/types.rs +304 -0
  45. package/crates/tish_compile_js/Cargo.toml +16 -0
  46. package/crates/tish_compile_js/examples/jsx_vdom_smoke.tish +8 -0
  47. package/crates/tish_compile_js/src/codegen.rs +794 -0
  48. package/crates/tish_compile_js/src/error.rs +20 -0
  49. package/crates/tish_compile_js/src/js_intrinsics.rs +82 -0
  50. package/crates/tish_compile_js/src/lib.rs +27 -0
  51. package/crates/tish_compile_js/src/tests_jsx.rs +32 -0
  52. package/crates/tish_compiler_wasm/Cargo.toml +19 -0
  53. package/crates/tish_compiler_wasm/src/lib.rs +55 -0
  54. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +462 -0
  55. package/crates/tish_core/Cargo.toml +11 -0
  56. package/crates/tish_core/src/console_style.rs +128 -0
  57. package/crates/tish_core/src/json.rs +327 -0
  58. package/crates/tish_core/src/lib.rs +15 -0
  59. package/crates/tish_core/src/macros.rs +37 -0
  60. package/crates/tish_core/src/uri.rs +115 -0
  61. package/crates/tish_core/src/value.rs +376 -0
  62. package/crates/tish_cranelift/Cargo.toml +17 -0
  63. package/crates/tish_cranelift/src/lib.rs +41 -0
  64. package/crates/tish_cranelift/src/link.rs +120 -0
  65. package/crates/tish_cranelift/src/lower.rs +77 -0
  66. package/crates/tish_cranelift_runtime/Cargo.toml +19 -0
  67. package/crates/tish_cranelift_runtime/src/lib.rs +43 -0
  68. package/crates/tish_eval/Cargo.toml +26 -0
  69. package/crates/tish_eval/src/eval.rs +3205 -0
  70. package/crates/tish_eval/src/http.rs +122 -0
  71. package/crates/tish_eval/src/lib.rs +59 -0
  72. package/crates/tish_eval/src/natives.rs +301 -0
  73. package/crates/tish_eval/src/promise.rs +173 -0
  74. package/crates/tish_eval/src/regex.rs +298 -0
  75. package/crates/tish_eval/src/timers.rs +111 -0
  76. package/crates/tish_eval/src/value.rs +224 -0
  77. package/crates/tish_eval/src/value_convert.rs +85 -0
  78. package/crates/tish_fmt/Cargo.toml +16 -0
  79. package/crates/tish_fmt/src/bin/tish-fmt.rs +41 -0
  80. package/crates/tish_fmt/src/lib.rs +884 -0
  81. package/crates/tish_jsx_web/Cargo.toml +7 -0
  82. package/crates/tish_jsx_web/README.md +18 -0
  83. package/crates/tish_jsx_web/src/lib.rs +157 -0
  84. package/crates/tish_jsx_web/vendor/Lattish.tish +347 -0
  85. package/crates/tish_lexer/Cargo.toml +7 -0
  86. package/crates/tish_lexer/src/lib.rs +430 -0
  87. package/crates/tish_lexer/src/token.rs +155 -0
  88. package/crates/tish_lint/Cargo.toml +17 -0
  89. package/crates/tish_lint/src/bin/tish-lint.rs +77 -0
  90. package/crates/tish_lint/src/lib.rs +278 -0
  91. package/crates/tish_llvm/Cargo.toml +11 -0
  92. package/crates/tish_llvm/src/lib.rs +106 -0
  93. package/crates/tish_lsp/Cargo.toml +22 -0
  94. package/crates/tish_lsp/README.md +26 -0
  95. package/crates/tish_lsp/src/main.rs +615 -0
  96. package/crates/tish_native/Cargo.toml +14 -0
  97. package/crates/tish_native/src/build.rs +102 -0
  98. package/crates/tish_native/src/lib.rs +237 -0
  99. package/crates/tish_opt/Cargo.toml +11 -0
  100. package/crates/tish_opt/src/lib.rs +896 -0
  101. package/crates/tish_parser/Cargo.toml +9 -0
  102. package/crates/tish_parser/src/lib.rs +123 -0
  103. package/crates/tish_parser/src/parser.rs +1714 -0
  104. package/crates/tish_runtime/Cargo.toml +26 -0
  105. package/crates/tish_runtime/src/http.rs +308 -0
  106. package/crates/tish_runtime/src/http_fetch.rs +453 -0
  107. package/crates/tish_runtime/src/lib.rs +1004 -0
  108. package/crates/tish_runtime/src/native_promise.rs +26 -0
  109. package/crates/tish_runtime/src/promise.rs +77 -0
  110. package/crates/tish_runtime/src/promise_io.rs +41 -0
  111. package/crates/tish_runtime/src/timers.rs +125 -0
  112. package/crates/tish_runtime/src/ws.rs +725 -0
  113. package/crates/tish_runtime/tests/fetch_readable_stream.rs +99 -0
  114. package/crates/tish_vm/Cargo.toml +31 -0
  115. package/crates/tish_vm/src/lib.rs +39 -0
  116. package/crates/tish_vm/src/vm.rs +1399 -0
  117. package/crates/tish_wasm/Cargo.toml +13 -0
  118. package/crates/tish_wasm/src/lib.rs +358 -0
  119. package/crates/tish_wasm_runtime/Cargo.toml +25 -0
  120. package/crates/tish_wasm_runtime/src/lib.rs +36 -0
  121. package/justfile +260 -0
  122. package/package.json +8 -3
  123. package/platform/darwin-arm64/tish +0 -0
  124. package/platform/darwin-x64/tish +0 -0
  125. package/platform/linux-arm64/tish +0 -0
  126. package/platform/linux-x64/tish +0 -0
  127. package/platform/win32-x64/tish.exe +0 -0
@@ -0,0 +1,26 @@
1
+ [package]
2
+ name = "tish_runtime"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ description = "Minimal runtime for Tish compiled output"
6
+
7
+ [features]
8
+ default = []
9
+ http = ["tokio", "reqwest", "futures", "bytes", "tiny_http", "tish_core/regex"]
10
+ fs = []
11
+ process = []
12
+ regex = ["tish_core/regex"]
13
+ # WebSocket (JS-like API). Modular: import { WebSocket, Server } from 'tish:ws'
14
+ ws = ["tokio", "tokio/net", "tokio/io-util", "tokio-tungstenite", "futures-util", "lazy_static"]
15
+
16
+ [dependencies]
17
+ tish_core = { path = "../tish_core" }
18
+ tish_builtins = { path = "../tish_builtins" }
19
+ tokio = { version = "1.50.0", features = ["rt-multi-thread", "macros", "time", "sync"], optional = true }
20
+ reqwest = { version = "0.13.2", default-features = false, features = ["rustls", "json", "stream"], optional = true }
21
+ futures = { version = "0.3.32", optional = true }
22
+ bytes = { version = "1", optional = true }
23
+ tiny_http = { version = "0.12.0", optional = true }
24
+ tokio-tungstenite = { version = "0.29", optional = true }
25
+ futures-util = { version = "0.3", optional = true }
26
+ lazy_static = { version = "1", optional = true }
@@ -0,0 +1,308 @@
1
+ //! HTTP server + shared request parsing. Client `fetch` lives in `http_fetch.rs`.
2
+
3
+ use std::cell::RefCell;
4
+ use std::collections::HashMap;
5
+ use std::fs::File;
6
+ use std::io::Write;
7
+ use std::rc::Rc;
8
+ use std::sync::Arc;
9
+ use tish_core::Value;
10
+ use tokio::runtime::Runtime;
11
+
12
+ thread_local! {
13
+ pub(crate) static RUNTIME: Runtime = tokio::runtime::Builder::new_multi_thread()
14
+ .worker_threads(4)
15
+ .enable_all()
16
+ .build()
17
+ .expect("Failed to create tokio runtime");
18
+ }
19
+
20
+ /// Block on a future on the HTTP runtime. Uses a dedicated thread to avoid deadlocks when
21
+ /// called from contexts that share or nest tokio runtimes (e.g. WS + HTTP both enabled).
22
+ pub(crate) fn block_on_http<F>(f: F) -> F::Output
23
+ where
24
+ F: std::future::Future + Send,
25
+ F::Output: Send,
26
+ {
27
+ std::thread::scope(|s| {
28
+ let (tx, rx) = std::sync::mpsc::channel();
29
+ s.spawn(move || {
30
+ let out = RUNTIME.with(|rt| rt.block_on(f));
31
+ let _ = tx.send(out);
32
+ });
33
+ rx.recv().expect("block_on_http thread panicked")
34
+ })
35
+ }
36
+
37
+ pub fn await_fetch(args: Vec<Value>) -> Value {
38
+ crate::native_promise::await_promise(crate::native_promise::fetch_promise(args))
39
+ }
40
+
41
+ pub fn await_fetch_all(args: Vec<Value>) -> Value {
42
+ crate::native_promise::await_promise(crate::native_promise::fetch_all_promise(args))
43
+ }
44
+
45
+ pub(crate) fn extract_method(options: Option<&Value>) -> String {
46
+ options
47
+ .and_then(|v| match v {
48
+ Value::Object(obj) => obj.borrow().get(&Arc::from("method")).cloned(),
49
+ _ => None,
50
+ })
51
+ .map(|v| v.to_display_string().to_uppercase())
52
+ .unwrap_or_else(|| "GET".to_string())
53
+ }
54
+
55
+ pub(crate) fn extract_headers(options: Option<&Value>) -> Vec<(String, String)> {
56
+ options
57
+ .and_then(|v| match v {
58
+ Value::Object(obj) => obj.borrow().get(&Arc::from("headers")).cloned(),
59
+ _ => None,
60
+ })
61
+ .map(|v| match v {
62
+ Value::Object(obj) => obj
63
+ .borrow()
64
+ .iter()
65
+ .map(|(k, v)| (k.to_string(), v.to_display_string()))
66
+ .collect(),
67
+ _ => vec![],
68
+ })
69
+ .unwrap_or_default()
70
+ }
71
+
72
+ pub(crate) fn extract_body(options: Option<&Value>) -> Option<String> {
73
+ options.and_then(|v| match v {
74
+ Value::Object(obj) => obj
75
+ .borrow()
76
+ .get(&Arc::from("body"))
77
+ .map(|v| v.to_display_string()),
78
+ _ => None,
79
+ })
80
+ }
81
+
82
+ pub(crate) fn build_error_response(error: &str) -> Value {
83
+ let mut obj: HashMap<Arc<str>, Value> = HashMap::with_capacity(2);
84
+ obj.insert(Arc::from("error"), Value::String(error.into()));
85
+ obj.insert(Arc::from("ok"), Value::Bool(false));
86
+ Value::Object(Rc::new(RefCell::new(obj)))
87
+ }
88
+
89
+ /// Start an HTTP server that handles requests using the provided handler function.
90
+ pub fn serve<F>(args: &[Value], handler: F) -> Value
91
+ where
92
+ F: Fn(&[Value]) -> Value,
93
+ {
94
+ let port = match args.first() {
95
+ Some(Value::Number(n)) => *n as u16,
96
+ _ => return build_error_response("serve requires a port number"),
97
+ };
98
+
99
+ let max_requests: Option<usize> = args.get(2).and_then(|v| match v {
100
+ Value::Number(n) if *n >= 1.0 => Some(*n as usize),
101
+ _ => None,
102
+ });
103
+
104
+ let server = match create_server(port) {
105
+ Ok(s) => s,
106
+ Err(e) => {
107
+ eprintln!("[tish http] Failed to bind: {}", e);
108
+ return build_error_response(&e);
109
+ }
110
+ };
111
+
112
+ println!("Server listening on http://0.0.0.0:{}", port);
113
+
114
+ if max_requests == Some(1) {
115
+ let port = port;
116
+ std::thread::spawn(move || {
117
+ std::thread::sleep(std::time::Duration::from_millis(50));
118
+ if let Ok(mut stream) = std::net::TcpStream::connect(format!("127.0.0.1:{}", port)) {
119
+ let _ = stream.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n");
120
+ let _ = stream.shutdown(std::net::Shutdown::Write);
121
+ }
122
+ });
123
+ }
124
+
125
+ let mut count = 0usize;
126
+ for mut request in server.incoming_requests() {
127
+ let req_value = request_to_value(&mut request);
128
+ let response_value = handler(&[req_value]);
129
+ if let Some((status, headers, file_path)) = extract_file_from_response(&response_value) {
130
+ send_file_response(request, status, headers, file_path);
131
+ } else {
132
+ let (status, headers, body) = value_to_response(&response_value);
133
+ send_response(request, status, headers, body);
134
+ }
135
+ count += 1;
136
+ if max_requests.map(|m| count >= m).unwrap_or(false) {
137
+ break;
138
+ }
139
+ }
140
+
141
+ Value::Null
142
+ }
143
+
144
+ pub fn create_server(port: u16) -> Result<tiny_http::Server, String> {
145
+ let addr = format!("0.0.0.0:{}", port);
146
+ tiny_http::Server::http(&addr).map_err(|e| format!("Failed to start server: {}", e))
147
+ }
148
+
149
+ pub fn request_to_value(request: &mut tiny_http::Request) -> Value {
150
+ let mut obj: HashMap<Arc<str>, Value> = HashMap::with_capacity(6);
151
+
152
+ obj.insert(
153
+ Arc::from("method"),
154
+ Value::String(request.method().to_string().into()),
155
+ );
156
+ obj.insert(
157
+ Arc::from("url"),
158
+ Value::String(request.url().to_string().into()),
159
+ );
160
+
161
+ let path = request.url().split('?').next().unwrap_or("/");
162
+ obj.insert(Arc::from("path"), Value::String(path.into()));
163
+
164
+ let query_string = request.url().split('?').nth(1).unwrap_or("");
165
+ obj.insert(Arc::from("query"), Value::String(query_string.into()));
166
+
167
+ let mut headers_obj: HashMap<Arc<str>, Value> = HashMap::with_capacity(request.headers().len());
168
+ for header in request.headers() {
169
+ headers_obj.insert(
170
+ Arc::from(header.field.as_str().as_str()),
171
+ Value::String(header.value.as_str().into()),
172
+ );
173
+ }
174
+ obj.insert(
175
+ Arc::from("headers"),
176
+ Value::Object(Rc::new(RefCell::new(headers_obj))),
177
+ );
178
+
179
+ let mut body = String::new();
180
+ let _ = request.as_reader().read_to_string(&mut body);
181
+ obj.insert(Arc::from("body"), Value::String(body.into()));
182
+
183
+ Value::Object(Rc::new(RefCell::new(obj)))
184
+ }
185
+
186
+ pub fn value_to_response(value: &Value) -> (u16, Vec<(String, String)>, String) {
187
+ let default_status = 200u16;
188
+ let default_body = String::new();
189
+
190
+ let (status, headers, body) = match value {
191
+ Value::Object(obj) => {
192
+ let obj_ref = obj.borrow();
193
+
194
+ let status = obj_ref
195
+ .get(&Arc::from("status"))
196
+ .and_then(|v| match v {
197
+ Value::Number(n) => Some(*n as u16),
198
+ _ => None,
199
+ })
200
+ .unwrap_or(default_status);
201
+
202
+ let has_error = obj_ref.contains_key(&Arc::from("error"));
203
+ let body = obj_ref
204
+ .get(&Arc::from("body"))
205
+ .map(|v| v.to_display_string())
206
+ .unwrap_or_else(|| {
207
+ obj_ref
208
+ .get(&Arc::from("error"))
209
+ .map(|v| v.to_display_string())
210
+ .unwrap_or_default()
211
+ });
212
+ let status = if has_error && status == default_status {
213
+ 500u16
214
+ } else {
215
+ status
216
+ };
217
+
218
+ let headers = obj_ref
219
+ .get(&Arc::from("headers"))
220
+ .and_then(|v| match v {
221
+ Value::Object(h) => Some(
222
+ h.borrow()
223
+ .iter()
224
+ .map(|(k, v)| (k.to_string(), v.to_display_string()))
225
+ .collect(),
226
+ ),
227
+ _ => None,
228
+ })
229
+ .unwrap_or_default();
230
+
231
+ (status, headers, body)
232
+ }
233
+ Value::String(s) => (default_status, vec![], s.to_string()),
234
+ _ => (default_status, vec![], default_body),
235
+ };
236
+
237
+ (status, headers, body)
238
+ }
239
+
240
+ /// If the response value has a "file" key, extract (status, headers, file_path) for streaming.
241
+ /// Used for binary files (e.g. .wasm) where readFile/body would fail.
242
+ fn extract_file_from_response(value: &Value) -> Option<(u16, Vec<(String, String)>, String)> {
243
+ let Value::Object(obj) = value else { return None };
244
+ let obj_ref = obj.borrow();
245
+ let Value::String(file_path) = obj_ref.get(&Arc::from("file"))? else { return None };
246
+ let file_path = file_path.to_string();
247
+ let status = obj_ref
248
+ .get(&Arc::from("status"))
249
+ .and_then(|v| match v { Value::Number(n) => Some(*n as u16), _ => None })
250
+ .unwrap_or(200);
251
+ let headers = obj_ref
252
+ .get(&Arc::from("headers"))
253
+ .and_then(|v| match v {
254
+ Value::Object(h) => Some(
255
+ h.borrow()
256
+ .iter()
257
+ .map(|(k, v)| (k.to_string(), v.to_display_string()))
258
+ .collect(),
259
+ ),
260
+ _ => None,
261
+ })
262
+ .unwrap_or_default();
263
+ Some((status, headers, file_path))
264
+ }
265
+
266
+ fn send_file_response(
267
+ request: tiny_http::Request,
268
+ status: u16,
269
+ headers: Vec<(String, String)>,
270
+ file_path: String,
271
+ ) {
272
+ let file = match File::open(&file_path) {
273
+ Ok(f) => f,
274
+ Err(e) => {
275
+ eprintln!("Failed to open file {}: {}", file_path, e);
276
+ let fallback = tiny_http::Response::from_string(format!("File not found: {}", file_path))
277
+ .with_status_code(tiny_http::StatusCode(500));
278
+ let _ = request.respond(fallback);
279
+ return;
280
+ }
281
+ };
282
+ let status_code = tiny_http::StatusCode(status);
283
+ let mut response = tiny_http::Response::from_file(file).with_status_code(status_code);
284
+ for (key, value) in headers {
285
+ if let Ok(header) = tiny_http::Header::from_bytes(key.as_bytes(), value.as_bytes()) {
286
+ response = response.with_header(header);
287
+ }
288
+ }
289
+ let _ = request.respond(response);
290
+ }
291
+
292
+ pub fn send_response(
293
+ request: tiny_http::Request,
294
+ status: u16,
295
+ headers: Vec<(String, String)>,
296
+ body: String,
297
+ ) {
298
+ let status_code = tiny_http::StatusCode(status);
299
+ let mut response = tiny_http::Response::from_string(body).with_status_code(status_code);
300
+
301
+ for (key, value) in headers {
302
+ if let Ok(header) = tiny_http::Header::from_bytes(key.as_bytes(), value.as_bytes()) {
303
+ response = response.with_header(header);
304
+ }
305
+ }
306
+
307
+ let _ = request.respond(response);
308
+ }