@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
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
//! Conversion between tishlang_eval::Value and tishlang_core::Value for opaque method calls.
|
|
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
|
|
|
8
|
-
use tishlang_core::Value as CoreValue;
|
|
7
|
+
use tishlang_core::{ObjectMap, Value as CoreValue};
|
|
9
8
|
|
|
10
|
-
use crate::value::Value;
|
|
9
|
+
use crate::value::{PropMap, Value};
|
|
11
10
|
|
|
12
11
|
/// Convert interpreter Value to core Value. Fails for interpreter-only variants.
|
|
13
12
|
pub fn eval_to_core(v: &Value) -> Result<CoreValue, String> {
|
|
@@ -24,7 +23,7 @@ pub fn eval_to_core(v: &Value) -> Result<CoreValue, String> {
|
|
|
24
23
|
Ok(CoreValue::Array(Rc::new(RefCell::new(out))))
|
|
25
24
|
}
|
|
26
25
|
Value::Object(map) => {
|
|
27
|
-
let mut out =
|
|
26
|
+
let mut out = ObjectMap::default();
|
|
28
27
|
for (k, v) in map.borrow().iter() {
|
|
29
28
|
out.insert(Arc::clone(k), eval_to_core(v)?);
|
|
30
29
|
}
|
|
@@ -53,7 +52,7 @@ pub fn core_to_eval(v: CoreValue) -> Value {
|
|
|
53
52
|
Value::Array(Rc::new(RefCell::new(out)))
|
|
54
53
|
}
|
|
55
54
|
CoreValue::Object(map) => {
|
|
56
|
-
let mut out =
|
|
55
|
+
let mut out = PropMap::default();
|
|
57
56
|
for (k, v) in map.borrow().iter() {
|
|
58
57
|
out.insert(Arc::clone(k), core_to_eval(v.clone()));
|
|
59
58
|
}
|
|
@@ -64,10 +63,7 @@ pub fn core_to_eval(v: CoreValue) -> Value {
|
|
|
64
63
|
CoreValue::Promise(p) => Value::CorePromise(Arc::clone(&p)),
|
|
65
64
|
#[cfg(not(feature = "http"))]
|
|
66
65
|
CoreValue::Promise(_) => Value::Null,
|
|
67
|
-
#[cfg(any(feature = "http", feature = "ws"))]
|
|
68
66
|
CoreValue::Function(f) => Value::CoreFn(Rc::clone(&f)),
|
|
69
|
-
#[cfg(not(any(feature = "http", feature = "ws")))]
|
|
70
|
-
CoreValue::Function(_) => Value::Null,
|
|
71
67
|
// tishlang_core gets regex from http or regex features; handle RegExp when it exists
|
|
72
68
|
#[cfg(any(feature = "http", feature = "regex"))]
|
|
73
69
|
CoreValue::RegExp(re) => {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
use tishlang_ast::{
|
|
4
4
|
ArrayElement, ArrowBody, BinOp, CallArg, CompoundOp, DestructElement, DestructPattern,
|
|
5
|
-
ExportDeclaration, Expr, ImportSpecifier, JsxAttrValue, JsxChild, JsxProp,
|
|
5
|
+
ExportDeclaration, Expr, FunParam, ImportSpecifier, JsxAttrValue, JsxChild, JsxProp,
|
|
6
6
|
Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Statement, TypeAnnotation,
|
|
7
7
|
TypedParam, UnaryOp,
|
|
8
8
|
};
|
|
@@ -385,19 +385,38 @@ impl Printer {
|
|
|
385
385
|
self.buf.push_str(" }");
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
-
fn param_list(&mut self, params: &[
|
|
388
|
+
fn param_list(&mut self, params: &[FunParam], rest: &Option<TypedParam>) {
|
|
389
389
|
for (i, p) in params.iter().enumerate() {
|
|
390
390
|
if i > 0 {
|
|
391
391
|
self.buf.push_str(", ");
|
|
392
392
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
393
|
+
match p {
|
|
394
|
+
FunParam::Simple(tp) => {
|
|
395
|
+
self.buf.push_str(tp.name.as_ref());
|
|
396
|
+
if let Some(t) = &tp.type_ann {
|
|
397
|
+
self.buf.push_str(": ");
|
|
398
|
+
self.type_ann(t);
|
|
399
|
+
}
|
|
400
|
+
if let Some(e) = &tp.default {
|
|
401
|
+
self.buf.push_str(" = ");
|
|
402
|
+
self.expr(e);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
FunParam::Destructure {
|
|
406
|
+
pattern,
|
|
407
|
+
type_ann,
|
|
408
|
+
default,
|
|
409
|
+
} => {
|
|
410
|
+
self.destruct_pat(pattern);
|
|
411
|
+
if let Some(t) = type_ann {
|
|
412
|
+
self.buf.push_str(": ");
|
|
413
|
+
self.type_ann(t);
|
|
414
|
+
}
|
|
415
|
+
if let Some(e) = default {
|
|
416
|
+
self.buf.push_str(" = ");
|
|
417
|
+
self.expr(e);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
401
420
|
}
|
|
402
421
|
}
|
|
403
422
|
if let Some(r) = rest {
|
|
@@ -551,6 +570,26 @@ impl Printer {
|
|
|
551
570
|
}
|
|
552
571
|
self.buf.push(')');
|
|
553
572
|
}
|
|
573
|
+
Expr::New { callee, args, .. } => {
|
|
574
|
+
self.buf.push_str("new ");
|
|
575
|
+
self.expr(callee);
|
|
576
|
+
if !args.is_empty() {
|
|
577
|
+
self.buf.push('(');
|
|
578
|
+
for (i, a) in args.iter().enumerate() {
|
|
579
|
+
if i > 0 {
|
|
580
|
+
self.buf.push_str(", ");
|
|
581
|
+
}
|
|
582
|
+
match a {
|
|
583
|
+
CallArg::Expr(ex) => self.expr(ex),
|
|
584
|
+
CallArg::Spread(ex) => {
|
|
585
|
+
self.buf.push_str("...");
|
|
586
|
+
self.expr(ex);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
self.buf.push(')');
|
|
591
|
+
}
|
|
592
|
+
}
|
|
554
593
|
Expr::Member {
|
|
555
594
|
object,
|
|
556
595
|
prop,
|
|
@@ -54,6 +54,7 @@ pub enum TokenKind {
|
|
|
54
54
|
In,
|
|
55
55
|
Async,
|
|
56
56
|
Await,
|
|
57
|
+
New,
|
|
57
58
|
Import,
|
|
58
59
|
Export,
|
|
59
60
|
|
|
@@ -149,6 +150,7 @@ impl TokenKind {
|
|
|
149
150
|
"in" => TokenKind::In,
|
|
150
151
|
"async" => TokenKind::Async,
|
|
151
152
|
"await" => TokenKind::Await,
|
|
153
|
+
"new" => TokenKind::New,
|
|
152
154
|
"import" => TokenKind::Import,
|
|
153
155
|
"export" => TokenKind::Export,
|
|
154
156
|
_ => TokenKind::Ident,
|
|
@@ -180,6 +180,15 @@ fn lint_expr(e: &Expr, out: &mut Vec<LintDiagnostic>) {
|
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
|
+
Expr::New { callee, args, .. } => {
|
|
184
|
+
lint_expr(callee, out);
|
|
185
|
+
for a in args {
|
|
186
|
+
match a {
|
|
187
|
+
tishlang_ast::CallArg::Expr(x) => lint_expr(x, out),
|
|
188
|
+
tishlang_ast::CallArg::Spread(x) => lint_expr(x, out),
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
183
192
|
Expr::Member { object, .. } => {
|
|
184
193
|
lint_expr(object, out);
|
|
185
194
|
}
|
|
@@ -45,6 +45,19 @@ pub fn build_via_cargo(
|
|
|
45
45
|
})
|
|
46
46
|
.collect();
|
|
47
47
|
|
|
48
|
+
let tish_ui_path = std::path::Path::new(&runtime_path)
|
|
49
|
+
.parent()
|
|
50
|
+
.ok_or_else(|| "invalid tishlang_runtime path (no parent)".to_string())?
|
|
51
|
+
.join("tish_ui");
|
|
52
|
+
let ui_dep = if rust_code.contains("tishlang_ui") {
|
|
53
|
+
format!(
|
|
54
|
+
"\ntishlang_ui = {{ path = {:?}, default-features = false, features = [\"runtime\"] }}\n",
|
|
55
|
+
tish_ui_path.display().to_string().replace('\\', "/")
|
|
56
|
+
)
|
|
57
|
+
} else {
|
|
58
|
+
String::new()
|
|
59
|
+
};
|
|
60
|
+
|
|
48
61
|
let cargo_toml = format!(
|
|
49
62
|
r#"[package]
|
|
50
63
|
name = "tish_output"
|
|
@@ -63,7 +76,7 @@ codegen-units = 1
|
|
|
63
76
|
lto = "thin"
|
|
64
77
|
|
|
65
78
|
[dependencies]
|
|
66
|
-
tishlang_runtime = {{ path = {:?}{} }}{}{}
|
|
79
|
+
tishlang_runtime = {{ path = {:?}{} }}{}{}{}
|
|
67
80
|
"#,
|
|
68
81
|
out_name,
|
|
69
82
|
runtime_path,
|
|
@@ -73,7 +86,8 @@ tishlang_runtime = {{ path = {:?}{} }}{}{}
|
|
|
73
86
|
String::new()
|
|
74
87
|
} else {
|
|
75
88
|
format!("\n{}", native_deps)
|
|
76
|
-
}
|
|
89
|
+
},
|
|
90
|
+
ui_dep
|
|
77
91
|
);
|
|
78
92
|
|
|
79
93
|
fs::write(build_dir.join("Cargo.toml"), cargo_toml)
|
|
@@ -339,6 +339,21 @@ fn optimize_expr(expr: &Expr) -> Expr {
|
|
|
339
339
|
.collect(),
|
|
340
340
|
span: *span,
|
|
341
341
|
},
|
|
342
|
+
Expr::New {
|
|
343
|
+
callee,
|
|
344
|
+
args,
|
|
345
|
+
span,
|
|
346
|
+
} => Expr::New {
|
|
347
|
+
callee: Box::new(optimize_expr(callee)),
|
|
348
|
+
args: args
|
|
349
|
+
.iter()
|
|
350
|
+
.map(|a| match a {
|
|
351
|
+
tishlang_ast::CallArg::Expr(e) => tishlang_ast::CallArg::Expr(optimize_expr(e)),
|
|
352
|
+
tishlang_ast::CallArg::Spread(e) => tishlang_ast::CallArg::Spread(optimize_expr(e)),
|
|
353
|
+
})
|
|
354
|
+
.collect(),
|
|
355
|
+
span: *span,
|
|
356
|
+
},
|
|
342
357
|
Expr::Member {
|
|
343
358
|
object,
|
|
344
359
|
prop,
|
|
@@ -18,7 +18,7 @@ pub fn parse(source: &str) -> Result<Program, String> {
|
|
|
18
18
|
#[cfg(test)]
|
|
19
19
|
mod tests {
|
|
20
20
|
use super::*;
|
|
21
|
-
use tishlang_ast::{Expr, ObjectProp, Statement};
|
|
21
|
+
use tishlang_ast::{CallArg, Expr, ObjectProp, Statement};
|
|
22
22
|
|
|
23
23
|
#[test]
|
|
24
24
|
fn test_async_fn_parse() {
|
|
@@ -120,4 +120,104 @@ mod tests {
|
|
|
120
120
|
_ => panic!("expected KeyValue prop"),
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
+
|
|
124
|
+
fn unwrap_expr_stmt(program: &tishlang_ast::Program) -> &Expr {
|
|
125
|
+
match program.statements.first() {
|
|
126
|
+
Some(Statement::ExprStmt { expr, .. }) => expr,
|
|
127
|
+
_ => panic!("expected expression statement"),
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
#[test]
|
|
132
|
+
fn new_expression_simple_call() {
|
|
133
|
+
let program = parse("new Foo()").expect("parse");
|
|
134
|
+
let e = unwrap_expr_stmt(&program);
|
|
135
|
+
match e {
|
|
136
|
+
Expr::New { callee, args, .. } => {
|
|
137
|
+
assert!(matches!(callee.as_ref(), Expr::Ident { name, .. } if name.as_ref() == "Foo"));
|
|
138
|
+
assert!(args.is_empty());
|
|
139
|
+
}
|
|
140
|
+
_ => panic!("expected New, got {:?}", e),
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
#[test]
|
|
145
|
+
fn new_expression_with_args() {
|
|
146
|
+
let program = parse("new Uint8Array(16)").expect("parse");
|
|
147
|
+
let e = unwrap_expr_stmt(&program);
|
|
148
|
+
match e {
|
|
149
|
+
Expr::New { callee, args, .. } => {
|
|
150
|
+
assert!(matches!(callee.as_ref(), Expr::Ident { name, .. } if name.as_ref() == "Uint8Array"));
|
|
151
|
+
assert_eq!(args.len(), 1);
|
|
152
|
+
assert!(matches!(&args[0], CallArg::Expr(Expr::Literal { .. })));
|
|
153
|
+
}
|
|
154
|
+
_ => panic!("expected New"),
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
#[test]
|
|
159
|
+
fn new_expression_member_callee() {
|
|
160
|
+
let program = parse("new ns.AudioContext()").expect("parse");
|
|
161
|
+
let e = unwrap_expr_stmt(&program);
|
|
162
|
+
match e {
|
|
163
|
+
Expr::New { callee, args, .. } => {
|
|
164
|
+
assert!(matches!(
|
|
165
|
+
callee.as_ref(),
|
|
166
|
+
Expr::Member { prop: tishlang_ast::MemberProp::Name(p), .. } if p.as_ref() == "AudioContext"
|
|
167
|
+
));
|
|
168
|
+
assert!(args.is_empty());
|
|
169
|
+
}
|
|
170
|
+
_ => panic!("expected New"),
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
#[test]
|
|
175
|
+
fn new_expression_chained_new() {
|
|
176
|
+
let program = parse("new new Date()").expect("parse");
|
|
177
|
+
let e = unwrap_expr_stmt(&program);
|
|
178
|
+
match e {
|
|
179
|
+
Expr::New { callee, args, .. } => {
|
|
180
|
+
assert!(args.is_empty());
|
|
181
|
+
match callee.as_ref() {
|
|
182
|
+
Expr::New { callee: inner, args: inner_args, .. } => {
|
|
183
|
+
assert!(matches!(inner.as_ref(), Expr::Ident { name, .. } if name.as_ref() == "Date"));
|
|
184
|
+
assert!(inner_args.is_empty());
|
|
185
|
+
}
|
|
186
|
+
_ => panic!("expected nested New"),
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
_ => panic!("expected New"),
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
#[test]
|
|
194
|
+
fn new_then_member_access() {
|
|
195
|
+
let program = parse("new Foo().bar").expect("parse");
|
|
196
|
+
let e = unwrap_expr_stmt(&program);
|
|
197
|
+
match e {
|
|
198
|
+
Expr::Member { object, prop: tishlang_ast::MemberProp::Name(p), .. } => {
|
|
199
|
+
assert_eq!(p.as_ref(), "bar");
|
|
200
|
+
match object.as_ref() {
|
|
201
|
+
Expr::New { callee, args, .. } => {
|
|
202
|
+
assert!(matches!(callee.as_ref(), Expr::Ident { name, .. } if name.as_ref() == "Foo"));
|
|
203
|
+
assert!(args.is_empty());
|
|
204
|
+
}
|
|
205
|
+
_ => panic!("expected New object"),
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
_ => panic!("expected Member"),
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
#[test]
|
|
213
|
+
fn new_with_spread_arg() {
|
|
214
|
+
let program = parse("new Foo(...xs)").expect("parse");
|
|
215
|
+
let e = unwrap_expr_stmt(&program);
|
|
216
|
+
match e {
|
|
217
|
+
Expr::New { args, .. } => {
|
|
218
|
+
assert!(matches!(&args[0], CallArg::Spread(Expr::Ident { name, .. }) if name.as_ref() == "xs"));
|
|
219
|
+
}
|
|
220
|
+
_ => panic!("expected New"),
|
|
221
|
+
}
|
|
222
|
+
}
|
|
123
223
|
}
|
|
@@ -53,9 +53,9 @@ macro_rules! binary_multi_op {
|
|
|
53
53
|
|
|
54
54
|
use tishlang_ast::{
|
|
55
55
|
ArrowBody, ArrayElement, BinOp, CallArg, CompoundOp, DestructElement, DestructPattern,
|
|
56
|
-
DestructProp, ExportDeclaration, Expr, ImportSpecifier, JsxAttrValue, JsxChild,
|
|
57
|
-
Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Span, Statement,
|
|
58
|
-
TypedParam, UnaryOp,
|
|
56
|
+
DestructProp, ExportDeclaration, Expr, FunParam, ImportSpecifier, JsxAttrValue, JsxChild,
|
|
57
|
+
JsxProp, Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Span, Statement,
|
|
58
|
+
TypeAnnotation, TypedParam, UnaryOp,
|
|
59
59
|
};
|
|
60
60
|
use tishlang_lexer::{Token, TokenKind};
|
|
61
61
|
|
|
@@ -363,6 +363,55 @@ impl<'a> Parser<'a> {
|
|
|
363
363
|
self.expect(TokenKind::RBrace)?;
|
|
364
364
|
Ok(DestructPattern::Object(props))
|
|
365
365
|
}
|
|
366
|
+
|
|
367
|
+
/// One formal parameter: `name`, `name: T`, `name = expr`, or a destructuring pattern.
|
|
368
|
+
fn parse_fun_param(&mut self) -> Result<FunParam, String> {
|
|
369
|
+
if matches!(
|
|
370
|
+
self.peek_kind(),
|
|
371
|
+
Some(TokenKind::LBracket | TokenKind::LBrace)
|
|
372
|
+
) {
|
|
373
|
+
let pattern = self.parse_destruct_pattern()?;
|
|
374
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
375
|
+
self.advance();
|
|
376
|
+
Some(self.parse_type_annotation()?)
|
|
377
|
+
} else {
|
|
378
|
+
None
|
|
379
|
+
};
|
|
380
|
+
let default = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
381
|
+
self.advance();
|
|
382
|
+
Some(self.parse_expr()?)
|
|
383
|
+
} else {
|
|
384
|
+
None
|
|
385
|
+
};
|
|
386
|
+
return Ok(FunParam::Destructure {
|
|
387
|
+
pattern,
|
|
388
|
+
type_ann,
|
|
389
|
+
default,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
let param_name = self
|
|
393
|
+
.expect(TokenKind::Ident)?
|
|
394
|
+
.literal
|
|
395
|
+
.clone()
|
|
396
|
+
.ok_or("Expected param name")?;
|
|
397
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
398
|
+
self.advance();
|
|
399
|
+
Some(self.parse_type_annotation()?)
|
|
400
|
+
} else {
|
|
401
|
+
None
|
|
402
|
+
};
|
|
403
|
+
let default = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
404
|
+
self.advance();
|
|
405
|
+
Some(self.parse_expr()?)
|
|
406
|
+
} else {
|
|
407
|
+
None
|
|
408
|
+
};
|
|
409
|
+
Ok(FunParam::Simple(TypedParam {
|
|
410
|
+
name: param_name,
|
|
411
|
+
type_ann,
|
|
412
|
+
default,
|
|
413
|
+
}))
|
|
414
|
+
}
|
|
366
415
|
|
|
367
416
|
/// Parse a type annotation (number, string, T[], {a: T}, etc.)
|
|
368
417
|
fn parse_type_annotation(&mut self) -> Result<TypeAnnotation, String> {
|
|
@@ -480,26 +529,7 @@ impl<'a> Parser<'a> {
|
|
|
480
529
|
}
|
|
481
530
|
break;
|
|
482
531
|
}
|
|
483
|
-
|
|
484
|
-
.expect(TokenKind::Ident)?
|
|
485
|
-
.literal
|
|
486
|
-
.clone()
|
|
487
|
-
.ok_or("Expected param name")?;
|
|
488
|
-
// Optional type annotation
|
|
489
|
-
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
490
|
-
self.advance();
|
|
491
|
-
Some(self.parse_type_annotation()?)
|
|
492
|
-
} else {
|
|
493
|
-
None
|
|
494
|
-
};
|
|
495
|
-
// Optional default value
|
|
496
|
-
let default = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
497
|
-
self.advance();
|
|
498
|
-
Some(self.parse_expr()?)
|
|
499
|
-
} else {
|
|
500
|
-
None
|
|
501
|
-
};
|
|
502
|
-
params.push(TypedParam { name: param_name, type_ann, default });
|
|
532
|
+
params.push(self.parse_fun_param()?);
|
|
503
533
|
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
504
534
|
self.expect(TokenKind::Comma)?;
|
|
505
535
|
}
|
|
@@ -1117,8 +1147,97 @@ impl<'a> Parser<'a> {
|
|
|
1117
1147
|
})
|
|
1118
1148
|
}
|
|
1119
1149
|
|
|
1120
|
-
|
|
1150
|
+
/// Member chain (`.`, `?.`, `[]`) without consuming a call `(...)`.
|
|
1151
|
+
fn parse_member_expression_no_call(&mut self) -> Result<Expr, String> {
|
|
1121
1152
|
let mut expr = self.parse_primary()?;
|
|
1153
|
+
while let Some(kind) = self.peek_kind() {
|
|
1154
|
+
match kind {
|
|
1155
|
+
TokenKind::Dot | TokenKind::OptionalChain => {
|
|
1156
|
+
let optional = kind == TokenKind::OptionalChain;
|
|
1157
|
+
self.advance();
|
|
1158
|
+
let prop = self
|
|
1159
|
+
.expect(TokenKind::Ident)?
|
|
1160
|
+
.literal
|
|
1161
|
+
.clone()
|
|
1162
|
+
.ok_or("Expected property name")?;
|
|
1163
|
+
let start = expr.span().start;
|
|
1164
|
+
let end = self.peek().map(|x| x.span.start).unwrap_or(start);
|
|
1165
|
+
expr = Expr::Member {
|
|
1166
|
+
object: Box::new(expr),
|
|
1167
|
+
prop: MemberProp::Name(prop),
|
|
1168
|
+
optional,
|
|
1169
|
+
span: Span { start, end },
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
TokenKind::LBracket => {
|
|
1173
|
+
self.advance();
|
|
1174
|
+
let index = self.parse_expr()?;
|
|
1175
|
+
self.expect(TokenKind::RBracket)?;
|
|
1176
|
+
let start = expr.span().start;
|
|
1177
|
+
let end = self.peek().map(|x| x.span.start).unwrap_or(start);
|
|
1178
|
+
expr = Expr::Index {
|
|
1179
|
+
object: Box::new(expr),
|
|
1180
|
+
index: Box::new(index),
|
|
1181
|
+
optional: false,
|
|
1182
|
+
span: Span { start, end },
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
_ => break,
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
Ok(expr)
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
/// ECMAScript `NewExpression`: `new` chains, then member expression without call, optional `(...)`.
|
|
1192
|
+
fn parse_new_expression(&mut self) -> Result<Expr, String> {
|
|
1193
|
+
if matches!(self.peek_kind(), Some(TokenKind::New)) {
|
|
1194
|
+
let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
1195
|
+
self.advance();
|
|
1196
|
+
let callee = Box::new(self.parse_new_expression()?);
|
|
1197
|
+
let args = if matches!(self.peek_kind(), Some(TokenKind::LParen)) {
|
|
1198
|
+
self.advance();
|
|
1199
|
+
let mut args = Vec::new();
|
|
1200
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1201
|
+
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
1202
|
+
self.advance();
|
|
1203
|
+
let arg_expr = self.parse_expr()?;
|
|
1204
|
+
args.push(CallArg::Spread(arg_expr));
|
|
1205
|
+
} else {
|
|
1206
|
+
let arg_expr = self.parse_expr()?;
|
|
1207
|
+
args.push(CallArg::Expr(arg_expr));
|
|
1208
|
+
}
|
|
1209
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1210
|
+
self.expect(TokenKind::Comma)?;
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
self.expect(TokenKind::RParen)?;
|
|
1214
|
+
args
|
|
1215
|
+
} else {
|
|
1216
|
+
Vec::new()
|
|
1217
|
+
};
|
|
1218
|
+
let end = self
|
|
1219
|
+
.peek()
|
|
1220
|
+
.map(|x| x.span.start)
|
|
1221
|
+
.unwrap_or(callee.as_ref().span().end);
|
|
1222
|
+
Ok(Expr::New {
|
|
1223
|
+
callee,
|
|
1224
|
+
args,
|
|
1225
|
+
span: Span {
|
|
1226
|
+
start: span_start,
|
|
1227
|
+
end,
|
|
1228
|
+
},
|
|
1229
|
+
})
|
|
1230
|
+
} else {
|
|
1231
|
+
self.parse_member_expression_no_call()
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
fn parse_postfix(&mut self) -> Result<Expr, String> {
|
|
1236
|
+
let mut expr = if matches!(self.peek_kind(), Some(TokenKind::New)) {
|
|
1237
|
+
self.parse_new_expression()?
|
|
1238
|
+
} else {
|
|
1239
|
+
self.parse_primary()?
|
|
1240
|
+
};
|
|
1122
1241
|
while let Some(kind) = self.peek_kind() {
|
|
1123
1242
|
match kind {
|
|
1124
1243
|
TokenKind::LParen => {
|
|
@@ -1243,7 +1362,11 @@ impl<'a> Parser<'a> {
|
|
|
1243
1362
|
let body = self.parse_arrow_body()?;
|
|
1244
1363
|
let end = self.previous_span_end();
|
|
1245
1364
|
return Ok(Expr::ArrowFunction {
|
|
1246
|
-
params: vec![TypedParam {
|
|
1365
|
+
params: vec![FunParam::Simple(TypedParam {
|
|
1366
|
+
name: name.clone(),
|
|
1367
|
+
type_ann: None,
|
|
1368
|
+
default: None,
|
|
1369
|
+
})],
|
|
1247
1370
|
body,
|
|
1248
1371
|
span: Span { start: span.start, end },
|
|
1249
1372
|
});
|
|
@@ -1418,39 +1541,26 @@ impl<'a> Parser<'a> {
|
|
|
1418
1541
|
is_arrow = true;
|
|
1419
1542
|
}
|
|
1420
1543
|
} else {
|
|
1421
|
-
// Try to parse params: (x, y,
|
|
1544
|
+
// Try to parse params: (x, y), ({ a }), ([a, b]), with optional types/defaults
|
|
1545
|
+
let mut params_ok = true;
|
|
1422
1546
|
loop {
|
|
1423
|
-
if
|
|
1424
|
-
break;
|
|
1547
|
+
if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1548
|
+
break;
|
|
1549
|
+
}
|
|
1550
|
+
match self.parse_fun_param() {
|
|
1551
|
+
Ok(param) => params.push(param),
|
|
1552
|
+
Err(_) => {
|
|
1553
|
+
params_ok = false;
|
|
1554
|
+
break;
|
|
1555
|
+
}
|
|
1425
1556
|
}
|
|
1426
|
-
let name = self.advance().unwrap().literal.clone().ok_or("Expected param name")?;
|
|
1427
|
-
|
|
1428
|
-
// Optional type annotation
|
|
1429
|
-
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
1430
|
-
self.advance();
|
|
1431
|
-
Some(self.parse_type_annotation()?)
|
|
1432
|
-
} else {
|
|
1433
|
-
None
|
|
1434
|
-
};
|
|
1435
|
-
|
|
1436
|
-
// Optional default value
|
|
1437
|
-
let default = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
1438
|
-
self.advance();
|
|
1439
|
-
Some(self.parse_expr()?)
|
|
1440
|
-
} else {
|
|
1441
|
-
None
|
|
1442
|
-
};
|
|
1443
|
-
|
|
1444
|
-
params.push(TypedParam { name, type_ann, default });
|
|
1445
|
-
|
|
1446
1557
|
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
1447
1558
|
self.advance();
|
|
1448
1559
|
} else {
|
|
1449
1560
|
break;
|
|
1450
1561
|
}
|
|
1451
1562
|
}
|
|
1452
|
-
|
|
1453
|
-
if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1563
|
+
if params_ok && matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1454
1564
|
self.advance(); // consume )
|
|
1455
1565
|
if matches!(self.peek_kind(), Some(TokenKind::Arrow)) {
|
|
1456
1566
|
self.advance(); // consume =>
|
|
@@ -1787,6 +1897,7 @@ impl ExprSpan for Expr {
|
|
|
1787
1897
|
Expr::Binary { span, .. } => *span,
|
|
1788
1898
|
Expr::Unary { span, .. } => *span,
|
|
1789
1899
|
Expr::Call { span, .. } => *span,
|
|
1900
|
+
Expr::New { span, .. } => *span,
|
|
1790
1901
|
Expr::Member { span, .. } => *span,
|
|
1791
1902
|
Expr::Index { span, .. } => *span,
|
|
1792
1903
|
Expr::Conditional { span, .. } => *span,
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
//! HTTP server + shared request parsing. Client `fetch` lives in `http_fetch.rs`.
|
|
2
2
|
|
|
3
3
|
use std::cell::RefCell;
|
|
4
|
-
use std::collections::HashMap;
|
|
5
4
|
use std::fs::File;
|
|
6
5
|
use std::io::Write;
|
|
7
6
|
use std::rc::Rc;
|
|
8
7
|
use std::sync::Arc;
|
|
9
|
-
use tishlang_core::Value;
|
|
8
|
+
use tishlang_core::{ObjectMap, Value};
|
|
10
9
|
use tokio::runtime::Runtime;
|
|
11
10
|
|
|
12
11
|
thread_local! {
|
|
@@ -80,7 +79,7 @@ pub(crate) fn extract_body(options: Option<&Value>) -> Option<String> {
|
|
|
80
79
|
}
|
|
81
80
|
|
|
82
81
|
pub(crate) fn build_error_response(error: &str) -> Value {
|
|
83
|
-
let mut obj:
|
|
82
|
+
let mut obj: ObjectMap = ObjectMap::with_capacity(2);
|
|
84
83
|
obj.insert(Arc::from("error"), Value::String(error.into()));
|
|
85
84
|
obj.insert(Arc::from("ok"), Value::Bool(false));
|
|
86
85
|
Value::Object(Rc::new(RefCell::new(obj)))
|
|
@@ -147,7 +146,7 @@ pub fn create_server(port: u16) -> Result<tiny_http::Server, String> {
|
|
|
147
146
|
}
|
|
148
147
|
|
|
149
148
|
pub fn request_to_value(request: &mut tiny_http::Request) -> Value {
|
|
150
|
-
let mut obj:
|
|
149
|
+
let mut obj: ObjectMap = ObjectMap::with_capacity(6);
|
|
151
150
|
|
|
152
151
|
obj.insert(
|
|
153
152
|
Arc::from("method"),
|
|
@@ -164,7 +163,7 @@ pub fn request_to_value(request: &mut tiny_http::Request) -> Value {
|
|
|
164
163
|
let query_string = request.url().split('?').nth(1).unwrap_or("");
|
|
165
164
|
obj.insert(Arc::from("query"), Value::String(query_string.into()));
|
|
166
165
|
|
|
167
|
-
let mut headers_obj:
|
|
166
|
+
let mut headers_obj: ObjectMap = ObjectMap::with_capacity(request.headers().len());
|
|
168
167
|
for header in request.headers() {
|
|
169
168
|
headers_obj.insert(
|
|
170
169
|
Arc::from(header.field.as_str().as_str()),
|