@tishlang/tish 1.4.2 → 1.6.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.
- package/bin/tish +0 -0
- package/crates/tish/Cargo.toml +2 -2
- package/crates/tish/src/cli_help.rs +504 -0
- package/crates/tish/src/main.rs +76 -90
- package/crates/tish/src/repl_completion.rs +1 -1
- package/crates/tish/tests/cargo_example_compile.rs +65 -0
- package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
- package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
- package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
- package/crates/tish/tests/integration_test.rs +48 -0
- package/crates/tish_build_utils/src/lib.rs +204 -1
- package/crates/tish_builtins/src/string.rs +248 -0
- package/crates/tish_bytecode/Cargo.toml +1 -0
- package/crates/tish_bytecode/src/compiler.rs +289 -66
- package/crates/tish_bytecode/src/opcode.rs +13 -3
- package/crates/tish_bytecode/src/peephole.rs +21 -16
- package/crates/tish_bytecode/tests/break_continue_bytecode.rs +44 -0
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +277 -93
- package/crates/tish_compile/src/lib.rs +7 -4
- package/crates/tish_compile/src/resolve.rs +418 -40
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +1 -0
- package/crates/tish_core/src/value.rs +1 -0
- package/crates/tish_eval/src/eval.rs +49 -1
- package/crates/tish_eval/src/lib.rs +1 -1
- package/crates/tish_native/src/build.rs +86 -17
- package/crates/tish_native/src/lib.rs +36 -16
- package/crates/tish_runtime/src/lib.rs +4 -0
- package/crates/tish_vm/src/lib.rs +1 -1
- package/crates/tish_vm/src/vm.rs +165 -19
- package/crates/tish_vm/tests/lexical_scope_declare.rs +34 -0
- 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_vm/src/vm.rs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
//! Stack-based bytecode VM.
|
|
2
2
|
|
|
3
3
|
use std::cell::RefCell;
|
|
4
|
+
use std::collections::HashSet;
|
|
4
5
|
use std::rc::Rc;
|
|
5
6
|
use std::sync::Arc;
|
|
6
7
|
|
|
@@ -15,15 +16,40 @@ use tishlang_core::{ObjectMap, Value};
|
|
|
15
16
|
|
|
16
17
|
type ArrayMethodFn = Rc<dyn Fn(&[Value]) -> Value>;
|
|
17
18
|
|
|
19
|
+
/// Feature names enabled for this VM run (`tish run --feature …`). `full` enables every optional capability.
|
|
20
|
+
#[cfg_attr(
|
|
21
|
+
not(any(feature = "fs", feature = "http", feature = "process", feature = "ws")),
|
|
22
|
+
allow(dead_code)
|
|
23
|
+
)]
|
|
24
|
+
fn cap_allows(enabled: &HashSet<String>, name: &str) -> bool {
|
|
25
|
+
enabled.contains("full") || enabled.contains(name)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// Capabilities linked into this `tishlang_vm` binary (compile-time). Used by [`Vm::new`] and `run()`.
|
|
29
|
+
pub fn all_compiled_capabilities() -> HashSet<String> {
|
|
30
|
+
#[allow(unused_mut)]
|
|
31
|
+
let mut s = HashSet::new();
|
|
32
|
+
#[cfg(feature = "http")]
|
|
33
|
+
s.insert("http".to_string());
|
|
34
|
+
#[cfg(feature = "fs")]
|
|
35
|
+
s.insert("fs".to_string());
|
|
36
|
+
#[cfg(feature = "process")]
|
|
37
|
+
s.insert("process".to_string());
|
|
38
|
+
#[cfg(feature = "regex")]
|
|
39
|
+
s.insert("regex".to_string());
|
|
40
|
+
#[cfg(feature = "ws")]
|
|
41
|
+
s.insert("ws".to_string());
|
|
42
|
+
s
|
|
43
|
+
}
|
|
44
|
+
|
|
18
45
|
/// Look up built-in module export for LoadNativeExport. Returns None if unknown or feature disabled.
|
|
19
|
-
/// Parameters are only used when the corresponding feature (fs, http, process) is enabled.
|
|
20
46
|
#[cfg_attr(
|
|
21
47
|
not(any(feature = "fs", feature = "http", feature = "process", feature = "ws")),
|
|
22
48
|
allow(unused_variables)
|
|
23
49
|
)]
|
|
24
|
-
fn get_builtin_export(spec: &str, export_name: &str) -> Option<Value> {
|
|
50
|
+
fn get_builtin_export(enabled: &HashSet<String>, spec: &str, export_name: &str) -> Option<Value> {
|
|
25
51
|
#[cfg(feature = "fs")]
|
|
26
|
-
if spec == "tish:fs" {
|
|
52
|
+
if spec == "tish:fs" && cap_allows(enabled, "fs") {
|
|
27
53
|
return match export_name {
|
|
28
54
|
"readFile" => Some(Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::read_file(args)))),
|
|
29
55
|
"writeFile" => Some(Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::write_file(args)))),
|
|
@@ -35,7 +61,7 @@ fn get_builtin_export(spec: &str, export_name: &str) -> Option<Value> {
|
|
|
35
61
|
};
|
|
36
62
|
}
|
|
37
63
|
#[cfg(feature = "http")]
|
|
38
|
-
if spec == "tish:http" {
|
|
64
|
+
if spec == "tish:http" && cap_allows(enabled, "http") {
|
|
39
65
|
return match export_name {
|
|
40
66
|
// Bytecode compiler lowers `await expr` to `tish:http.await(promise)` (see tish_bytecode compiler).
|
|
41
67
|
"await" => Some(Value::Function(Rc::new(|args: &[Value]| {
|
|
@@ -59,7 +85,7 @@ fn get_builtin_export(spec: &str, export_name: &str) -> Option<Value> {
|
|
|
59
85
|
};
|
|
60
86
|
}
|
|
61
87
|
#[cfg(feature = "process")]
|
|
62
|
-
if spec == "tish:process" {
|
|
88
|
+
if spec == "tish:process" && cap_allows(enabled, "process") {
|
|
63
89
|
return match export_name {
|
|
64
90
|
"exit" => Some(Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_exit(args)))),
|
|
65
91
|
"cwd" => Some(Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_cwd(args)))),
|
|
@@ -97,7 +123,7 @@ fn get_builtin_export(spec: &str, export_name: &str) -> Option<Value> {
|
|
|
97
123
|
};
|
|
98
124
|
}
|
|
99
125
|
#[cfg(feature = "ws")]
|
|
100
|
-
if spec == "tish:ws" {
|
|
126
|
+
if spec == "tish:ws" && cap_allows(enabled, "ws") {
|
|
101
127
|
return match export_name {
|
|
102
128
|
"WebSocket" => Some(Value::Function(Rc::new(|args: &[Value]| {
|
|
103
129
|
tishlang_runtime::web_socket_client(args)
|
|
@@ -149,7 +175,8 @@ fn vm_log_err(s: &str) {
|
|
|
149
175
|
}
|
|
150
176
|
|
|
151
177
|
/// Initialize default globals (console, Math, JSON, etc.)
|
|
152
|
-
|
|
178
|
+
#[allow(unused_variables)]
|
|
179
|
+
fn init_globals(enabled: &HashSet<String>) -> ObjectMap {
|
|
153
180
|
let mut g = ObjectMap::default();
|
|
154
181
|
|
|
155
182
|
let mut console = ObjectMap::default();
|
|
@@ -381,7 +408,7 @@ fn init_globals() -> ObjectMap {
|
|
|
381
408
|
g.insert("document".into(), Value::Object(Rc::new(RefCell::new(document_obj))));
|
|
382
409
|
|
|
383
410
|
#[cfg(feature = "process")]
|
|
384
|
-
{
|
|
411
|
+
if cap_allows(enabled, "process") {
|
|
385
412
|
let mut process_obj = ObjectMap::default();
|
|
386
413
|
process_obj.insert(
|
|
387
414
|
"exit".into(),
|
|
@@ -413,7 +440,7 @@ fn init_globals() -> ObjectMap {
|
|
|
413
440
|
}
|
|
414
441
|
|
|
415
442
|
#[cfg(feature = "http")]
|
|
416
|
-
{
|
|
443
|
+
if cap_allows(enabled, "http") {
|
|
417
444
|
g.insert(
|
|
418
445
|
"serve".into(),
|
|
419
446
|
Value::Function(Rc::new(|args: &[Value]| {
|
|
@@ -433,21 +460,44 @@ fn init_globals() -> ObjectMap {
|
|
|
433
460
|
/// Shared scope for closure capture (parent frame's locals).
|
|
434
461
|
type ScopeMap = Rc<RefCell<ObjectMap>>;
|
|
435
462
|
|
|
463
|
+
/// Options for the convenience [`run_with_options`] helper (one-shot VM run from the CLI).
|
|
464
|
+
#[derive(Clone, Debug, Default)]
|
|
465
|
+
pub struct VmRunOptions {
|
|
466
|
+
/// When true and not inside a nested chunk (`enclosing` is `None`), top-level [`Opcode::DeclareVar`]
|
|
467
|
+
/// also writes to globals so the REPL keeps bindings across input lines.
|
|
468
|
+
pub repl_mode: bool,
|
|
469
|
+
/// Enabled capabilities for this run (e.g. `fs`, `http`, `full`). Empty = none (secure default).
|
|
470
|
+
pub capabilities: HashSet<String>,
|
|
471
|
+
}
|
|
472
|
+
|
|
436
473
|
pub struct Vm {
|
|
437
474
|
stack: Vec<Value>,
|
|
438
475
|
scope: ObjectMap,
|
|
439
476
|
/// Enclosing scope for closures (captured parent frame locals).
|
|
440
477
|
enclosing: Option<ScopeMap>,
|
|
441
478
|
globals: Rc<RefCell<ObjectMap>>,
|
|
479
|
+
/// Capabilities for `LoadNativeExport` and globals such as `process` / `serve`.
|
|
480
|
+
capabilities: Arc<HashSet<String>>,
|
|
442
481
|
}
|
|
443
482
|
|
|
444
483
|
impl Vm {
|
|
484
|
+
/// VM with every capability that exists in this `tishlang_vm` build (embedders, tests, `run()`).
|
|
445
485
|
pub fn new() -> Self {
|
|
486
|
+
Self::with_capabilities_arc(Arc::new(all_compiled_capabilities()))
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/// VM with an explicit capability set (e.g. from `tish run --feature …`).
|
|
490
|
+
pub fn with_capabilities(capabilities: HashSet<String>) -> Self {
|
|
491
|
+
Self::with_capabilities_arc(Arc::new(capabilities))
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
fn with_capabilities_arc(capabilities: Arc<HashSet<String>>) -> Self {
|
|
446
495
|
Self {
|
|
447
496
|
stack: Vec::new(),
|
|
448
497
|
scope: ObjectMap::default(),
|
|
449
498
|
enclosing: None,
|
|
450
|
-
globals: Rc::new(RefCell::new(init_globals())),
|
|
499
|
+
globals: Rc::new(RefCell::new(init_globals(capabilities.as_ref()))),
|
|
500
|
+
capabilities,
|
|
451
501
|
}
|
|
452
502
|
}
|
|
453
503
|
|
|
@@ -476,7 +526,12 @@ impl Vm {
|
|
|
476
526
|
}
|
|
477
527
|
|
|
478
528
|
pub fn run(&mut self, chunk: &Chunk) -> Result<Value, String> {
|
|
479
|
-
self.
|
|
529
|
+
self.run_with_options(chunk, false)
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/// Run a chunk using this VM's capability set. `repl_mode` persists top-level `let` across REPL lines.
|
|
533
|
+
pub fn run_with_options(&mut self, chunk: &Chunk, repl_mode: bool) -> Result<Value, String> {
|
|
534
|
+
self.run_chunk(chunk, &chunk.nested, &[], repl_mode)
|
|
480
535
|
}
|
|
481
536
|
|
|
482
537
|
fn run_chunk(
|
|
@@ -484,6 +539,7 @@ impl Vm {
|
|
|
484
539
|
chunk: &Chunk,
|
|
485
540
|
nested: &[Chunk],
|
|
486
541
|
args: &[Value],
|
|
542
|
+
repl_mode: bool,
|
|
487
543
|
) -> Result<Value, String> {
|
|
488
544
|
let code = &chunk.code;
|
|
489
545
|
let constants = &chunk.constants;
|
|
@@ -517,6 +573,7 @@ impl Vm {
|
|
|
517
573
|
}
|
|
518
574
|
}
|
|
519
575
|
let mut try_handlers: Vec<(usize, usize)> = vec![];
|
|
576
|
+
let mut block_undo_stack: Vec<Vec<(Arc<str>, Option<Value>)>> = vec![];
|
|
520
577
|
|
|
521
578
|
loop {
|
|
522
579
|
if ip >= code.len() {
|
|
@@ -548,14 +605,16 @@ impl Vm {
|
|
|
548
605
|
let inner_clone = inner.clone();
|
|
549
606
|
let globals = Rc::clone(&self.globals);
|
|
550
607
|
let enclosing = Some(Rc::clone(&local_scope));
|
|
608
|
+
let capabilities = Arc::clone(&self.capabilities);
|
|
551
609
|
Value::Function(Rc::new(move |args: &[Value]| {
|
|
552
610
|
let mut vm = Vm {
|
|
553
611
|
stack: Vec::new(),
|
|
554
612
|
scope: ObjectMap::default(),
|
|
555
613
|
enclosing: enclosing.clone(),
|
|
556
614
|
globals: Rc::clone(&globals),
|
|
615
|
+
capabilities: Arc::clone(&capabilities),
|
|
557
616
|
};
|
|
558
|
-
vm.run_chunk(&inner_clone, &inner_clone.nested, args)
|
|
617
|
+
vm.run_chunk(&inner_clone, &inner_clone.nested, args, false)
|
|
559
618
|
.unwrap_or(Value::Null)
|
|
560
619
|
}))
|
|
561
620
|
}
|
|
@@ -616,6 +675,62 @@ impl Vm {
|
|
|
616
675
|
}
|
|
617
676
|
}
|
|
618
677
|
}
|
|
678
|
+
Opcode::DeclareVar => {
|
|
679
|
+
let idx = Self::read_u16(code, &mut ip);
|
|
680
|
+
let name = names
|
|
681
|
+
.get(idx as usize)
|
|
682
|
+
.ok_or_else(|| format!("Name index out of bounds: {}", idx))?;
|
|
683
|
+
let v = self
|
|
684
|
+
.stack
|
|
685
|
+
.pop()
|
|
686
|
+
.ok_or_else(|| "Stack underflow".to_string())?;
|
|
687
|
+
if let Some(frame) = block_undo_stack.last_mut() {
|
|
688
|
+
let old = local_scope.borrow().get(name.as_ref()).cloned();
|
|
689
|
+
frame.push((Arc::clone(name), old));
|
|
690
|
+
}
|
|
691
|
+
// REPL: persist top-level bindings only (not block-locals shadowing globals).
|
|
692
|
+
if repl_mode && self.enclosing.is_none() && block_undo_stack.is_empty() {
|
|
693
|
+
self.globals
|
|
694
|
+
.borrow_mut()
|
|
695
|
+
.insert(Arc::clone(name), v.clone());
|
|
696
|
+
}
|
|
697
|
+
local_scope.borrow_mut().insert(Arc::clone(name), v);
|
|
698
|
+
}
|
|
699
|
+
Opcode::DeclareVarPlain => {
|
|
700
|
+
let idx = Self::read_u16(code, &mut ip);
|
|
701
|
+
let name = names
|
|
702
|
+
.get(idx as usize)
|
|
703
|
+
.ok_or_else(|| format!("Name index out of bounds: {}", idx))?;
|
|
704
|
+
let v = self
|
|
705
|
+
.stack
|
|
706
|
+
.pop()
|
|
707
|
+
.ok_or_else(|| "Stack underflow".to_string())?;
|
|
708
|
+
if repl_mode && self.enclosing.is_none() && block_undo_stack.is_empty() {
|
|
709
|
+
self.globals
|
|
710
|
+
.borrow_mut()
|
|
711
|
+
.insert(Arc::clone(name), v.clone());
|
|
712
|
+
}
|
|
713
|
+
local_scope.borrow_mut().insert(Arc::clone(name), v);
|
|
714
|
+
}
|
|
715
|
+
Opcode::EnterBlock => {
|
|
716
|
+
block_undo_stack.push(Vec::new());
|
|
717
|
+
}
|
|
718
|
+
Opcode::ExitBlock => {
|
|
719
|
+
let frame = block_undo_stack.pop().ok_or_else(|| {
|
|
720
|
+
"ExitBlock without matching EnterBlock".to_string()
|
|
721
|
+
})?;
|
|
722
|
+
for (name, old) in frame.into_iter().rev() {
|
|
723
|
+
let mut ls = local_scope.borrow_mut();
|
|
724
|
+
match old {
|
|
725
|
+
Some(prev) => {
|
|
726
|
+
ls.insert(name, prev);
|
|
727
|
+
}
|
|
728
|
+
None => {
|
|
729
|
+
ls.remove(name.as_ref());
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
619
734
|
Opcode::LoadGlobal => {
|
|
620
735
|
let idx = Self::read_u16(code, &mut ip);
|
|
621
736
|
let name = names
|
|
@@ -1144,11 +1259,18 @@ impl Vm {
|
|
|
1144
1259
|
return Err("LoadNativeExport: export_name constant out of bounds or not string".to_string());
|
|
1145
1260
|
}
|
|
1146
1261
|
};
|
|
1147
|
-
let v = get_builtin_export(spec, export_name).ok_or_else(|| {
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1262
|
+
let v = get_builtin_export(self.capabilities.as_ref(), spec, export_name).ok_or_else(|| {
|
|
1263
|
+
if spec.starts_with("cargo:") {
|
|
1264
|
+
format!(
|
|
1265
|
+
"cargo:… imports are only supported by `tish build` with the Rust native backend (not the bytecode VM). Spec: {}",
|
|
1266
|
+
spec
|
|
1267
|
+
)
|
|
1268
|
+
} else {
|
|
1269
|
+
format!(
|
|
1270
|
+
"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.",
|
|
1271
|
+
spec, export_name
|
|
1272
|
+
)
|
|
1273
|
+
}
|
|
1152
1274
|
})?;
|
|
1153
1275
|
self.stack.push(v);
|
|
1154
1276
|
}
|
|
@@ -1379,6 +1501,12 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
|
|
|
1379
1501
|
}
|
|
1380
1502
|
Value::String(s) => {
|
|
1381
1503
|
let key_s = key.as_ref();
|
|
1504
|
+
if let Ok(idx) = key_s.parse::<usize>() {
|
|
1505
|
+
return match s.chars().nth(idx) {
|
|
1506
|
+
Some(c) => Ok(Value::String(Arc::from(c.to_string()))),
|
|
1507
|
+
None => Err("Index out of bounds".to_string()),
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1382
1510
|
if key_s == "length" {
|
|
1383
1511
|
return Ok(Value::Number(s.chars().count() as f64));
|
|
1384
1512
|
}
|
|
@@ -1389,6 +1517,18 @@ fn get_member(obj: &Value, key: &Arc<str>) -> Result<Value, String> {
|
|
|
1389
1517
|
let from = args.get(1);
|
|
1390
1518
|
str_builtins::index_of(&Value::String(Arc::clone(&s_clone)), search, from)
|
|
1391
1519
|
}),
|
|
1520
|
+
"lastIndexOf" => Rc::new(move |args: &[Value]| {
|
|
1521
|
+
let search = args.first().unwrap_or(&Value::Null);
|
|
1522
|
+
let position = args
|
|
1523
|
+
.get(1)
|
|
1524
|
+
.cloned()
|
|
1525
|
+
.unwrap_or(Value::Number(f64::INFINITY));
|
|
1526
|
+
str_builtins::last_index_of(
|
|
1527
|
+
&Value::String(Arc::clone(&s_clone)),
|
|
1528
|
+
search,
|
|
1529
|
+
&position,
|
|
1530
|
+
)
|
|
1531
|
+
}),
|
|
1392
1532
|
"includes" => Rc::new(move |args: &[Value]| {
|
|
1393
1533
|
let search = args.first().unwrap_or(&Value::Null);
|
|
1394
1534
|
let from = args.get(1);
|
|
@@ -1490,8 +1630,14 @@ fn set_index(obj: &Value, idx: &Value, val: Value) -> Result<(), String> {
|
|
|
1490
1630
|
set_member(obj, &key, val)
|
|
1491
1631
|
}
|
|
1492
1632
|
|
|
1493
|
-
/// Run a chunk
|
|
1633
|
+
/// Run a chunk with every capability linked into this `tishlang_vm` build (tests, embedders).
|
|
1494
1634
|
pub fn run(chunk: &Chunk) -> Result<Value, String> {
|
|
1495
1635
|
let mut vm = Vm::new();
|
|
1496
|
-
vm.
|
|
1636
|
+
vm.run_with_options(chunk, false)
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
/// Run a chunk with options (e.g. REPL persistence for top-level declarations).
|
|
1640
|
+
pub fn run_with_options(chunk: &Chunk, opts: VmRunOptions) -> Result<Value, String> {
|
|
1641
|
+
let mut vm = Vm::with_capabilities(opts.capabilities);
|
|
1642
|
+
vm.run_with_options(chunk, opts.repl_mode)
|
|
1497
1643
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//! `DeclareVar` + block scopes: function `let` shadows script-level names (bytecode VM).
|
|
2
|
+
|
|
3
|
+
use tishlang_bytecode::compile;
|
|
4
|
+
use tishlang_vm::run;
|
|
5
|
+
|
|
6
|
+
#[test]
|
|
7
|
+
fn declare_var_shadows_script_let_inside_fn() {
|
|
8
|
+
let src = r#"
|
|
9
|
+
let x = 1
|
|
10
|
+
fn f() {
|
|
11
|
+
let x = 2
|
|
12
|
+
return x
|
|
13
|
+
}
|
|
14
|
+
let r = f()
|
|
15
|
+
console.log("script", x, "fn", r)
|
|
16
|
+
"#;
|
|
17
|
+
let program = tishlang_parser::parse(src).expect("parse");
|
|
18
|
+
let chunk = compile(&program).expect("compile");
|
|
19
|
+
run(&chunk).expect("run");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#[test]
|
|
23
|
+
fn block_let_restores_outer_binding() {
|
|
24
|
+
let src = r#"
|
|
25
|
+
let x = 1
|
|
26
|
+
{
|
|
27
|
+
let x = 2
|
|
28
|
+
}
|
|
29
|
+
console.log(x)
|
|
30
|
+
"#;
|
|
31
|
+
let program = tishlang_parser::parse(src).expect("parse");
|
|
32
|
+
let chunk = compile(&program).expect("compile");
|
|
33
|
+
run(&chunk).expect("run");
|
|
34
|
+
}
|
package/package.json
CHANGED
|
Binary file
|
package/platform/darwin-x64/tish
CHANGED
|
Binary file
|
|
Binary file
|
package/platform/linux-x64/tish
CHANGED
|
Binary file
|
|
Binary file
|