@tishlang/tish 1.0.29 → 1.0.33
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.
- package/Cargo.toml +1 -0
- package/crates/js_to_tish/src/transform/expr.rs +15 -6
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/main.rs +1 -1
- package/crates/tish/tests/integration_test.rs +4 -3
- package/crates/tish_ast/src/ast.rs +65 -2
- package/crates/tish_build_utils/src/lib.rs +10 -2
- package/crates/tish_builtins/src/construct.rs +177 -0
- package/crates/tish_builtins/src/globals.rs +3 -5
- package/crates/tish_builtins/src/helpers.rs +2 -3
- package/crates/tish_builtins/src/lib.rs +1 -0
- package/crates/tish_builtins/src/object.rs +3 -4
- package/crates/tish_bytecode/src/compiler.rs +85 -11
- package/crates/tish_bytecode/src/opcode.rs +7 -3
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +233 -71
- package/crates/tish_compile/src/lib.rs +35 -0
- package/crates/tish_compile_js/Cargo.toml +1 -0
- package/crates/tish_compile_js/src/codegen.rs +38 -94
- package/crates/tish_compile_js/src/lib.rs +0 -1
- package/crates/tish_compile_js/src/tests_jsx.rs +68 -0
- package/crates/tish_core/Cargo.toml +4 -0
- package/crates/tish_core/src/console_style.rs +7 -1
- package/crates/tish_core/src/json.rs +1 -2
- package/crates/tish_core/src/macros.rs +2 -3
- package/crates/tish_core/src/value.rs +10 -5
- package/crates/tish_eval/Cargo.toml +2 -0
- package/crates/tish_eval/src/eval.rs +149 -72
- package/crates/tish_eval/src/http.rs +3 -4
- package/crates/tish_eval/src/regex.rs +3 -2
- package/crates/tish_eval/src/value.rs +11 -13
- package/crates/tish_eval/src/value_convert.rs +4 -8
- package/crates/tish_fmt/src/lib.rs +49 -10
- package/crates/tish_lexer/src/token.rs +2 -0
- package/crates/tish_lint/src/lib.rs +9 -0
- package/crates/tish_lsp/README.md +1 -1
- package/crates/tish_native/src/build.rs +16 -2
- package/crates/tish_opt/src/lib.rs +15 -0
- package/crates/tish_parser/src/lib.rs +101 -1
- package/crates/tish_parser/src/parser.rs +161 -50
- package/crates/tish_runtime/src/http.rs +4 -5
- package/crates/tish_runtime/src/http_fetch.rs +9 -10
- package/crates/tish_runtime/src/lib.rs +9 -2
- package/crates/tish_runtime/src/promise.rs +2 -3
- package/crates/tish_runtime/src/promise_io.rs +2 -3
- package/crates/tish_runtime/src/ws.rs +7 -7
- package/crates/tish_ui/Cargo.toml +17 -0
- package/crates/tish_ui/src/jsx.rs +390 -0
- package/crates/tish_ui/src/lib.rs +16 -0
- package/crates/tish_ui/src/runtime/hooks.rs +122 -0
- package/crates/tish_ui/src/runtime/mod.rs +173 -0
- package/crates/tish_vm/src/vm.rs +121 -27
- package/justfile +3 -3
- package/package.json +1 -1
- package/platform/darwin-arm64/tish +0 -0
- package/platform/darwin-x64/tish +0 -0
- package/platform/linux-arm64/tish +0 -0
- package/platform/linux-x64/tish +0 -0
- package/platform/win32-x64/tish.exe +0 -0
- package/crates/tish_compile_js/src/js_intrinsics.rs +0 -82
|
@@ -53,6 +53,41 @@ for (let i = 0; i < 5; i = i + 1) {
|
|
|
53
53
|
);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
#[test]
|
|
57
|
+
fn new_expression_lowers_to_construct_on_native() {
|
|
58
|
+
let src = "fn f() { return new Uint8Array(4) }";
|
|
59
|
+
let program = parse(src).unwrap();
|
|
60
|
+
let rust = compile(&program).unwrap();
|
|
61
|
+
assert!(
|
|
62
|
+
rust.contains("tish_construct"),
|
|
63
|
+
"expected new to lower to tish_construct, got snippet missing it"
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// User-defined constructor name: `new ClassName(...)` must compile natively (host `construct`)
|
|
68
|
+
/// and is the same surface syntax as the JS target (`new` in emitted JavaScript).
|
|
69
|
+
#[test]
|
|
70
|
+
fn new_class_name_compiles_native_via_tish_construct() {
|
|
71
|
+
let src = r#"
|
|
72
|
+
fn ClassName(x) {
|
|
73
|
+
return x
|
|
74
|
+
}
|
|
75
|
+
fn factory() {
|
|
76
|
+
return new ClassName(42)
|
|
77
|
+
}
|
|
78
|
+
"#;
|
|
79
|
+
let program = parse(src).unwrap();
|
|
80
|
+
let rust = compile(&program).unwrap();
|
|
81
|
+
assert!(
|
|
82
|
+
rust.contains("tish_construct"),
|
|
83
|
+
"expected new ClassName to lower to tish_construct"
|
|
84
|
+
);
|
|
85
|
+
assert!(
|
|
86
|
+
rust.contains("ClassName"),
|
|
87
|
+
"expected emitted Rust to reference ClassName callable"
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
56
91
|
#[test]
|
|
57
92
|
fn loop_var_decl_clone_via_project_full() {
|
|
58
93
|
let manifest = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
@@ -14,3 +14,4 @@ tishlang_ast = { path = "../tish_ast", version = ">=0.1" }
|
|
|
14
14
|
tishlang_compile = { path = "../tish_compile", version = ">=0.1" }
|
|
15
15
|
tishlang_opt = { path = "../tish_opt", version = ">=0.1" }
|
|
16
16
|
tishlang_parser = { path = "../tish_parser", version = ">=0.1" }
|
|
17
|
+
tishlang_ui = { path = "../tish_ui", default-features = false, features = ["compiler"] }
|
|
@@ -2,18 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
use tishlang_ast::{
|
|
4
4
|
ArrayElement, ArrowBody, BinOp, CallArg, CompoundOp, DestructElement, DestructPattern, Expr,
|
|
5
|
-
|
|
6
|
-
Statement, UnaryOp,
|
|
5
|
+
FunParam, Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Statement, UnaryOp,
|
|
7
6
|
};
|
|
8
7
|
|
|
9
8
|
use crate::error::CompileError;
|
|
10
|
-
use crate::js_intrinsics::{JsIntrinsic, JsIntrinsics};
|
|
11
9
|
|
|
12
10
|
struct Codegen {
|
|
13
11
|
output: String,
|
|
14
12
|
indent: usize,
|
|
15
13
|
in_async: bool,
|
|
16
|
-
intrinsics: JsIntrinsics,
|
|
17
14
|
}
|
|
18
15
|
|
|
19
16
|
fn stmt_terminates_switch(stmt: Option<&Statement>) -> bool {
|
|
@@ -29,7 +26,6 @@ impl Codegen {
|
|
|
29
26
|
output: String::new(),
|
|
30
27
|
indent: 0,
|
|
31
28
|
in_async: false,
|
|
32
|
-
intrinsics: JsIntrinsics::new(),
|
|
33
29
|
}
|
|
34
30
|
}
|
|
35
31
|
|
|
@@ -61,9 +57,6 @@ impl Codegen {
|
|
|
61
57
|
for stmt in &program.statements {
|
|
62
58
|
self.emit_statement(stmt)?;
|
|
63
59
|
}
|
|
64
|
-
self.output = self
|
|
65
|
-
.intrinsics
|
|
66
|
-
.prepend_runtime_preamble(std::mem::take(&mut self.output));
|
|
67
60
|
Ok(())
|
|
68
61
|
}
|
|
69
62
|
|
|
@@ -304,20 +297,34 @@ impl Codegen {
|
|
|
304
297
|
|
|
305
298
|
fn emit_params(
|
|
306
299
|
&mut self,
|
|
307
|
-
params: &[
|
|
300
|
+
params: &[FunParam],
|
|
308
301
|
rest_param: Option<&tishlang_ast::TypedParam>,
|
|
309
302
|
) -> Result<String, CompileError> {
|
|
310
|
-
let mut parts: Vec<String> =
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
303
|
+
let mut parts: Vec<String> = Vec::new();
|
|
304
|
+
for p in params {
|
|
305
|
+
match p {
|
|
306
|
+
FunParam::Simple(tp) => {
|
|
307
|
+
let n = Self::escape_ident(tp.name.as_ref());
|
|
308
|
+
let s = if let Some(ref d) = tp.default {
|
|
309
|
+
format!("{} = {}", n, self.emit_expr(d)?)
|
|
310
|
+
} else {
|
|
311
|
+
n
|
|
312
|
+
};
|
|
313
|
+
parts.push(s);
|
|
314
|
+
}
|
|
315
|
+
FunParam::Destructure {
|
|
316
|
+
pattern,
|
|
317
|
+
type_ann: _,
|
|
318
|
+
default,
|
|
319
|
+
} => {
|
|
320
|
+
let mut s = self.emit_destruct_pattern(pattern)?;
|
|
321
|
+
if let Some(ref d) = default {
|
|
322
|
+
s = format!("{} = {}", s, self.emit_expr(d)?);
|
|
323
|
+
}
|
|
324
|
+
parts.push(s);
|
|
318
325
|
}
|
|
319
|
-
}
|
|
320
|
-
|
|
326
|
+
}
|
|
327
|
+
}
|
|
321
328
|
if let Some(rest) = rest_param {
|
|
322
329
|
parts.push(format!("...{}", Self::escape_ident(rest.name.as_ref())));
|
|
323
330
|
}
|
|
@@ -421,16 +428,6 @@ impl Codegen {
|
|
|
421
428
|
}
|
|
422
429
|
}
|
|
423
430
|
Expr::Call { callee, args, .. } => {
|
|
424
|
-
if let Some(kind) =
|
|
425
|
-
JsIntrinsics::classify_call(callee.as_ref(), args)?
|
|
426
|
-
{
|
|
427
|
-
self.intrinsics.mark(kind);
|
|
428
|
-
if kind == JsIntrinsic::Uint8Array {
|
|
429
|
-
let n = self.emit_call_arg(&args[0])?;
|
|
430
|
-
return Ok(JsIntrinsics::emit_expr(kind, &n));
|
|
431
|
-
}
|
|
432
|
-
return Ok(JsIntrinsics::emit_expr(kind, ""));
|
|
433
|
-
}
|
|
434
431
|
let c = self.emit_expr(callee)?;
|
|
435
432
|
let arg_strs: Result<Vec<_>, _> =
|
|
436
433
|
args.iter().map(|a| self.emit_call_arg(a)).collect();
|
|
@@ -438,6 +435,13 @@ impl Codegen {
|
|
|
438
435
|
// Tish uses null for undefined (e.g. empty array pop/shift)
|
|
439
436
|
format!("({}({}) ?? null)", c, arg_strs)
|
|
440
437
|
}
|
|
438
|
+
Expr::New { callee, args, .. } => {
|
|
439
|
+
let c = self.emit_expr(callee)?;
|
|
440
|
+
let arg_strs: Result<Vec<_>, _> =
|
|
441
|
+
args.iter().map(|a| self.emit_call_arg(a)).collect();
|
|
442
|
+
let arg_strs = arg_strs?.join(", ");
|
|
443
|
+
format!("(new {}({}) ?? null)", c, arg_strs)
|
|
444
|
+
}
|
|
441
445
|
Expr::Member {
|
|
442
446
|
object,
|
|
443
447
|
prop,
|
|
@@ -625,23 +629,11 @@ impl Codegen {
|
|
|
625
629
|
let o = self.emit_expr(operand)?;
|
|
626
630
|
format!("(await {})", o)
|
|
627
631
|
}
|
|
628
|
-
Expr::JsxElement {
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
};
|
|
634
|
-
let props_str = self.emit_jsx_props(props)?;
|
|
635
|
-
let children_strs: Result<Vec<_>, _> =
|
|
636
|
-
children.iter().map(|c| self.emit_jsx_child(c)).collect();
|
|
637
|
-
let children_str = children_strs?.join(", ");
|
|
638
|
-
format!("h({}, {}, [{}])", tag_str, props_str, children_str)
|
|
639
|
-
}
|
|
640
|
-
Expr::JsxFragment { children, .. } => {
|
|
641
|
-
let children_strs: Result<Vec<_>, _> =
|
|
642
|
-
children.iter().map(|c| self.emit_jsx_child(c)).collect();
|
|
643
|
-
let children_str = children_strs?.join(", ");
|
|
644
|
-
format!("h(Fragment, null, [{}])", children_str)
|
|
632
|
+
Expr::JsxElement { .. } | Expr::JsxFragment { .. } => {
|
|
633
|
+
tishlang_ui::jsx::emit_jsx_js(expr, &mut |e| {
|
|
634
|
+
self.emit_expr(e).map_err(|ce| ce.message)
|
|
635
|
+
})
|
|
636
|
+
.map_err(|m| CompileError { message: m })?
|
|
645
637
|
}
|
|
646
638
|
Expr::NativeModuleLoad { spec, .. } => {
|
|
647
639
|
return Err(CompileError {
|
|
@@ -661,54 +653,6 @@ impl Codegen {
|
|
|
661
653
|
}
|
|
662
654
|
}
|
|
663
655
|
|
|
664
|
-
fn emit_jsx_props(&mut self, props: &[JsxProp]) -> Result<String, CompileError> {
|
|
665
|
-
if props.is_empty() {
|
|
666
|
-
return Ok("null".to_string());
|
|
667
|
-
}
|
|
668
|
-
let parts: Result<Vec<_>, _> = props
|
|
669
|
-
.iter()
|
|
670
|
-
.map(|p| match p {
|
|
671
|
-
JsxProp::Attr { name, value } => {
|
|
672
|
-
let val = match value {
|
|
673
|
-
JsxAttrValue::String(s) => format!("{:?}", s.as_ref()),
|
|
674
|
-
JsxAttrValue::Expr(e) => self.emit_expr(e)?,
|
|
675
|
-
JsxAttrValue::ImplicitTrue => "true".to_string(),
|
|
676
|
-
};
|
|
677
|
-
let key = name.as_ref();
|
|
678
|
-
Ok(if key.chars().all(|c| c.is_alphanumeric() || c == '_') {
|
|
679
|
-
format!("{}: {}", key, val)
|
|
680
|
-
} else {
|
|
681
|
-
format!("{:?}: {}", key, val)
|
|
682
|
-
})
|
|
683
|
-
}
|
|
684
|
-
JsxProp::Spread(e) => Ok(format!("...{}", self.emit_expr(e)?)),
|
|
685
|
-
})
|
|
686
|
-
.collect();
|
|
687
|
-
Ok(format!("{{ {} }}", parts?.join(", ")))
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
fn emit_jsx_child(&mut self, child: &JsxChild) -> Result<String, CompileError> {
|
|
691
|
-
match child {
|
|
692
|
-
JsxChild::Text(s) => Ok(format!("{:?}", s.as_ref())),
|
|
693
|
-
JsxChild::Expr(e) => {
|
|
694
|
-
let inner = self.emit_expr(e)?;
|
|
695
|
-
// Only wrap literals we know are primitives (number, bool, null). Never wrap:
|
|
696
|
-
// string/template (already strings), JSX (elements), Call (components), Array/Ident (may hold elements).
|
|
697
|
-
let needs_string = matches!(
|
|
698
|
-
e,
|
|
699
|
-
Expr::Literal {
|
|
700
|
-
value: Literal::Number(_) | Literal::Bool(_) | Literal::Null,
|
|
701
|
-
..
|
|
702
|
-
}
|
|
703
|
-
);
|
|
704
|
-
Ok(if needs_string {
|
|
705
|
-
format!("String({})", inner)
|
|
706
|
-
} else {
|
|
707
|
-
inner
|
|
708
|
-
})
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
656
|
}
|
|
713
657
|
|
|
714
658
|
/// Compile a single program (no imports) to JavaScript. JSX lowers to `h` / `Fragment` (Lattish).
|
|
@@ -211,4 +211,72 @@ fn FileList() {
|
|
|
211
211
|
&js[..900.min(js.len())]
|
|
212
212
|
);
|
|
213
213
|
}
|
|
214
|
+
|
|
215
|
+
#[test]
|
|
216
|
+
fn new_date_global_emits_valid_js_with_and_without_optimize() {
|
|
217
|
+
let src = "let epoch = new Date(0)\nconsole.log(epoch.getTime())";
|
|
218
|
+
let program = parse(src).expect("parse");
|
|
219
|
+
for optimize in [false, true] {
|
|
220
|
+
let js = compile_with_jsx(&program, optimize).expect("compile");
|
|
221
|
+
assert!(
|
|
222
|
+
js.contains("new Date(0)"),
|
|
223
|
+
"optimize={optimize}: expected `new Date(0)` in JS output:\n{js}"
|
|
224
|
+
);
|
|
225
|
+
assert!(
|
|
226
|
+
!js.contains("let epoch = new;"),
|
|
227
|
+
"optimize={optimize}: broken `new` emission (missing constructor):\n{js}"
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
#[test]
|
|
233
|
+
fn new_uint8array_emits_direct_new_no_preamble() {
|
|
234
|
+
let src = "fn f(n) { return new Uint8Array(n) }";
|
|
235
|
+
let program = parse(src).expect("parse");
|
|
236
|
+
let js = compile_with_jsx(&program, false).expect("compile");
|
|
237
|
+
assert!(
|
|
238
|
+
js.contains("new Uint8Array("),
|
|
239
|
+
"expected direct new Uint8Array, got: {}",
|
|
240
|
+
&js[..500.min(js.len())]
|
|
241
|
+
);
|
|
242
|
+
assert!(
|
|
243
|
+
!js.contains("__tishUint8Array"),
|
|
244
|
+
"should not emit legacy intrinsic helper"
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
#[test]
|
|
249
|
+
fn new_audio_context_emits_direct_new_no_preamble() {
|
|
250
|
+
let src = "fn f() { return new AudioContext() }";
|
|
251
|
+
let program = parse(src).expect("parse");
|
|
252
|
+
let js = compile_with_jsx(&program, false).expect("compile");
|
|
253
|
+
assert!(
|
|
254
|
+
js.contains("new AudioContext("),
|
|
255
|
+
"expected new AudioContext, got: {}",
|
|
256
|
+
&js[..500.min(js.len())]
|
|
257
|
+
);
|
|
258
|
+
assert!(
|
|
259
|
+
!js.contains("__tishWebAudioCreateContext"),
|
|
260
|
+
"should not emit legacy intrinsic helper"
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
#[test]
|
|
265
|
+
fn new_class_name_emits_direct_new_js() {
|
|
266
|
+
let src = r#"
|
|
267
|
+
fn ClassName(x) {
|
|
268
|
+
return x
|
|
269
|
+
}
|
|
270
|
+
fn factory() {
|
|
271
|
+
return new ClassName(42)
|
|
272
|
+
}
|
|
273
|
+
"#;
|
|
274
|
+
let program = parse(src).expect("parse");
|
|
275
|
+
let js = compile_with_jsx(&program, false).expect("compile");
|
|
276
|
+
assert!(
|
|
277
|
+
js.contains("new ClassName("),
|
|
278
|
+
"expected new ClassName( in JS output, got: {}",
|
|
279
|
+
&js[..800.min(js.len())]
|
|
280
|
+
);
|
|
281
|
+
}
|
|
214
282
|
}
|
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
//! booleans, null, and object structure are easier to scan.
|
|
5
5
|
|
|
6
6
|
use std::io::IsTerminal;
|
|
7
|
+
use std::sync::OnceLock;
|
|
7
8
|
|
|
8
9
|
use crate::Value;
|
|
9
10
|
|
|
11
|
+
static CONSOLE_USES_COLORS: OnceLock<bool> = OnceLock::new();
|
|
12
|
+
|
|
10
13
|
/// ANSI escape codes (standard 4-bit + bright black for dim).
|
|
11
14
|
const RESET: &str = "\x1b[0m";
|
|
12
15
|
/// Number: yellow (Node-style)
|
|
@@ -25,8 +28,11 @@ const PUNCT: &str = "\x1b[90m";
|
|
|
25
28
|
const SPECIAL: &str = "\x1b[90m";
|
|
26
29
|
|
|
27
30
|
/// Returns whether console output should use colors (stdout is a TTY).
|
|
31
|
+
///
|
|
32
|
+
/// Cached for the process lifetime. `is_terminal()` is a syscall; benchmarks and
|
|
33
|
+
/// scripts with many `console.log` calls must not pay it on every line.
|
|
28
34
|
pub fn use_console_colors() -> bool {
|
|
29
|
-
std::io::stdout().is_terminal()
|
|
35
|
+
*CONSOLE_USES_COLORS.get_or_init(|| std::io::stdout().is_terminal())
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
/// Format a single value for console with optional ANSI colors (Node/Bun-style).
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
use crate::Value;
|
|
4
4
|
use std::cell::RefCell;
|
|
5
|
-
use std::collections::HashMap;
|
|
6
5
|
use std::rc::Rc;
|
|
7
6
|
use std::sync::Arc;
|
|
8
7
|
|
|
@@ -260,7 +259,7 @@ fn parse_array(input: &str) -> Result<(Value, &str), String> {
|
|
|
260
259
|
|
|
261
260
|
fn parse_object(input: &str) -> Result<(Value, &str), String> {
|
|
262
261
|
let mut input = &input[1..]; // skip '{'
|
|
263
|
-
let mut map =
|
|
262
|
+
let mut map = crate::ObjectMap::default();
|
|
264
263
|
|
|
265
264
|
input = input.trim_start();
|
|
266
265
|
if let Some(rest) = input.strip_prefix('}') {
|
|
@@ -24,11 +24,10 @@
|
|
|
24
24
|
macro_rules! tish_module {
|
|
25
25
|
($($name:expr => $fn:expr),* $(,)?) => {{
|
|
26
26
|
use std::cell::RefCell;
|
|
27
|
-
use std::collections::HashMap;
|
|
28
27
|
use std::rc::Rc;
|
|
29
28
|
use std::sync::Arc;
|
|
30
|
-
use $crate::Value;
|
|
31
|
-
let mut map =
|
|
29
|
+
use $crate::{ObjectMap, Value};
|
|
30
|
+
let mut map = ObjectMap::default();
|
|
32
31
|
$(
|
|
33
32
|
map.insert(Arc::from($name), Value::Function(Rc::new($fn)));
|
|
34
33
|
)*
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
//! Unified Value type for Tish runtime values.
|
|
2
2
|
|
|
3
3
|
use std::cell::RefCell;
|
|
4
|
-
use std::collections::HashMap;
|
|
5
4
|
use std::rc::Rc;
|
|
6
5
|
use std::sync::Arc;
|
|
7
6
|
|
|
7
|
+
use ahash::AHashMap;
|
|
8
|
+
|
|
9
|
+
/// Property map for objects and other `Arc<str>` → `Value` tables (VM globals, scopes).
|
|
10
|
+
/// Uses a faster hasher than `std::collections::HashMap` for string-heavy workloads.
|
|
11
|
+
pub type ObjectMap = AHashMap<Arc<str>, Value>;
|
|
12
|
+
|
|
8
13
|
#[cfg(feature = "regex")]
|
|
9
14
|
use fancy_regex::Regex;
|
|
10
15
|
|
|
@@ -147,7 +152,7 @@ pub enum Value {
|
|
|
147
152
|
Bool(bool),
|
|
148
153
|
Null,
|
|
149
154
|
Array(Rc<RefCell<Vec<Value>>>),
|
|
150
|
-
Object(Rc<RefCell<
|
|
155
|
+
Object(Rc<RefCell<ObjectMap>>),
|
|
151
156
|
Function(NativeFn),
|
|
152
157
|
#[cfg(feature = "regex")]
|
|
153
158
|
RegExp(Rc<RefCell<TishRegExp>>),
|
|
@@ -256,8 +261,8 @@ impl Value {
|
|
|
256
261
|
Value::Array(Rc::new(RefCell::new(items)))
|
|
257
262
|
}
|
|
258
263
|
|
|
259
|
-
/// Create a new object Value from a
|
|
260
|
-
pub fn object(map:
|
|
264
|
+
/// Create a new object Value from a property map.
|
|
265
|
+
pub fn object(map: ObjectMap) -> Self {
|
|
261
266
|
Value::Object(Rc::new(RefCell::new(map)))
|
|
262
267
|
}
|
|
263
268
|
|
|
@@ -268,7 +273,7 @@ impl Value {
|
|
|
268
273
|
|
|
269
274
|
/// Create an empty object Value.
|
|
270
275
|
pub fn empty_object() -> Self {
|
|
271
|
-
Value::Object(Rc::new(RefCell::new(
|
|
276
|
+
Value::Object(Rc::new(RefCell::new(ObjectMap::default())))
|
|
272
277
|
}
|
|
273
278
|
|
|
274
279
|
/// Extract the number value, if this is a Number.
|
|
@@ -16,8 +16,10 @@ tokio = ["dep:tokio"]
|
|
|
16
16
|
ws = ["dep:tishlang_runtime", "tishlang_runtime/ws"]
|
|
17
17
|
|
|
18
18
|
[dependencies]
|
|
19
|
+
ahash = "0.8.12"
|
|
19
20
|
rand = "0.10.0"
|
|
20
21
|
tishlang_ast = { path = "../tish_ast", version = ">=0.1" }
|
|
22
|
+
tishlang_builtins = { path = "../tish_builtins", version = ">=0.1" }
|
|
21
23
|
tishlang_parser = { path = "../tish_parser", version = ">=0.1" }
|
|
22
24
|
tishlang_core = { path = "../tish_core", version = ">=0.1" }
|
|
23
25
|
reqwest = { version = "0.13.2", default-features = false, features = ["rustls", "json", "stream"], optional = true }
|