@tishlang/tish 1.6.0 → 1.8.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/Cargo.toml +2 -0
- package/README.md +2 -0
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/error.rs +2 -8
- package/crates/js_to_tish/src/transform/expr.rs +128 -137
- package/crates/js_to_tish/src/transform/stmt.rs +62 -32
- package/crates/tish/Cargo.toml +15 -5
- package/crates/tish/src/cargo_native_registry.rs +29 -0
- package/crates/tish/src/cli_help.rs +92 -39
- package/crates/tish/src/main.rs +172 -86
- package/crates/tish/src/repl_completion.rs +3 -3
- package/crates/tish/tests/cargo_example_compile.rs +4 -2
- package/crates/tish/tests/integration_test.rs +216 -54
- package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
- package/crates/tish/tests/shortcircuit.rs +20 -5
- package/crates/tish_ast/src/ast.rs +92 -23
- package/crates/tish_build_utils/Cargo.toml +4 -0
- package/crates/tish_build_utils/src/lib.rs +136 -8
- package/crates/tish_builtins/Cargo.toml +5 -1
- package/crates/tish_builtins/src/array.rs +65 -33
- package/crates/tish_builtins/src/construct.rs +34 -39
- package/crates/tish_builtins/src/globals.rs +42 -26
- package/crates/tish_builtins/src/helpers.rs +2 -1
- package/crates/tish_builtins/src/lib.rs +5 -5
- package/crates/tish_builtins/src/math.rs +5 -3
- package/crates/tish_builtins/src/object.rs +3 -2
- package/crates/tish_builtins/src/string.rs +144 -22
- package/crates/tish_bytecode/src/chunk.rs +0 -1
- package/crates/tish_bytecode/src/compiler.rs +173 -71
- package/crates/tish_bytecode/src/opcode.rs +24 -6
- package/crates/tish_bytecode/src/peephole.rs +2 -2
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +1621 -453
- package/crates/tish_compile/src/infer.rs +75 -19
- package/crates/tish_compile/src/lib.rs +19 -8
- package/crates/tish_compile/src/resolve.rs +278 -137
- package/crates/tish_compile/src/types.rs +184 -24
- package/crates/tish_compile_js/Cargo.toml +1 -0
- package/crates/tish_compile_js/src/codegen.rs +181 -37
- package/crates/tish_compile_js/src/lib.rs +3 -1
- package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
- package/crates/tish_compiler_wasm/src/lib.rs +16 -13
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +69 -59
- package/crates/tish_core/Cargo.toml +8 -0
- package/crates/tish_core/src/json.rs +107 -56
- package/crates/tish_core/src/lib.rs +4 -2
- package/crates/tish_core/src/macros.rs +5 -5
- package/crates/tish_core/src/uri.rs +9 -6
- package/crates/tish_core/src/value.rs +145 -43
- package/crates/tish_core/src/vmref.rs +178 -0
- package/crates/tish_cranelift/src/link.rs +6 -9
- package/crates/tish_cranelift/src/lower.rs +14 -8
- package/crates/tish_eval/Cargo.toml +17 -2
- package/crates/tish_eval/src/eval.rs +474 -165
- package/crates/tish_eval/src/http.rs +61 -0
- package/crates/tish_eval/src/lib.rs +12 -8
- package/crates/tish_eval/src/natives.rs +136 -38
- package/crates/tish_eval/src/promise.rs +14 -8
- package/crates/tish_eval/src/timers.rs +28 -19
- package/crates/tish_eval/src/value.rs +17 -6
- package/crates/tish_eval/src/value_convert.rs +13 -5
- package/crates/tish_fmt/src/lib.rs +149 -43
- package/crates/tish_lexer/src/lib.rs +232 -63
- package/crates/tish_lexer/src/token.rs +10 -6
- package/crates/tish_llvm/src/lib.rs +17 -8
- package/crates/tish_lsp/Cargo.toml +4 -1
- package/crates/tish_lsp/README.md +1 -1
- package/crates/tish_lsp/src/builtin_goto.rs +261 -0
- package/crates/tish_lsp/src/import_goto.rs +549 -0
- package/crates/tish_lsp/src/main.rs +504 -106
- package/crates/tish_native/src/build.rs +4 -8
- package/crates/tish_native/src/lib.rs +54 -21
- package/crates/tish_opt/src/lib.rs +84 -52
- package/crates/tish_parser/src/lib.rs +45 -13
- package/crates/tish_parser/src/parser.rs +505 -130
- package/crates/tish_resolve/Cargo.toml +13 -0
- package/crates/tish_resolve/src/lib.rs +3436 -0
- package/crates/tish_resolve/src/pos.rs +133 -0
- package/crates/tish_runtime/Cargo.toml +68 -3
- package/crates/tish_runtime/src/http.rs +1136 -145
- package/crates/tish_runtime/src/http_fetch.rs +38 -27
- package/crates/tish_runtime/src/http_hyper.rs +418 -0
- package/crates/tish_runtime/src/http_prefork.rs +189 -0
- package/crates/tish_runtime/src/lib.rs +375 -189
- package/crates/tish_runtime/src/promise.rs +199 -40
- package/crates/tish_runtime/src/promise_io.rs +2 -1
- package/crates/tish_runtime/src/timers.rs +37 -1
- package/crates/tish_runtime/src/ws.rs +65 -42
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
- package/crates/tish_ui/src/jsx.rs +317 -27
- package/crates/tish_ui/src/lib.rs +5 -2
- package/crates/tish_ui/src/runtime/hooks.rs +406 -45
- package/crates/tish_ui/src/runtime/mod.rs +36 -9
- package/crates/tish_vm/Cargo.toml +15 -5
- package/crates/tish_vm/src/vm.rs +725 -281
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +11 -4
- package/crates/tish_wasm/src/lib.rs +55 -42
- package/crates/tish_wasm_runtime/Cargo.toml +2 -1
- package/crates/tish_wasm_runtime/src/lib.rs +1 -1
- package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +120 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +350 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
- package/justfile +8 -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
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
mod token;
|
|
7
7
|
|
|
8
|
-
pub use token::{Token, TokenKind
|
|
8
|
+
pub use token::{Span, Token, TokenKind};
|
|
9
9
|
|
|
10
10
|
use std::collections::VecDeque;
|
|
11
11
|
use std::iter::Peekable;
|
|
@@ -74,14 +74,21 @@ impl<'a> Lexer<'a> {
|
|
|
74
74
|
loop {
|
|
75
75
|
match self.peek() {
|
|
76
76
|
None | Some('{') | Some('<') => break,
|
|
77
|
-
Some(c) => {
|
|
77
|
+
Some(c) => {
|
|
78
|
+
self.advance();
|
|
79
|
+
s.push(c);
|
|
80
|
+
}
|
|
78
81
|
}
|
|
79
82
|
}
|
|
80
83
|
if s.is_empty() {
|
|
81
84
|
Ok(None)
|
|
82
85
|
} else {
|
|
83
86
|
let end = self.span_start();
|
|
84
|
-
Ok(Some(Token {
|
|
87
|
+
Ok(Some(Token {
|
|
88
|
+
kind: TokenKind::JsxText,
|
|
89
|
+
span: Span { start, end },
|
|
90
|
+
literal: Some(s.into()),
|
|
91
|
+
}))
|
|
85
92
|
}
|
|
86
93
|
}
|
|
87
94
|
|
|
@@ -139,7 +146,9 @@ impl<'a> Lexer<'a> {
|
|
|
139
146
|
|
|
140
147
|
fn skip_line_comment(&mut self) {
|
|
141
148
|
while let Some(c) = self.advance() {
|
|
142
|
-
if c == '\n' {
|
|
149
|
+
if c == '\n' {
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
143
152
|
}
|
|
144
153
|
}
|
|
145
154
|
|
|
@@ -147,8 +156,14 @@ impl<'a> Lexer<'a> {
|
|
|
147
156
|
let mut depth = 1;
|
|
148
157
|
while depth > 0 {
|
|
149
158
|
match self.advance() {
|
|
150
|
-
Some('*') if self.peek() == Some('/') => {
|
|
151
|
-
|
|
159
|
+
Some('*') if self.peek() == Some('/') => {
|
|
160
|
+
self.advance();
|
|
161
|
+
depth -= 1;
|
|
162
|
+
}
|
|
163
|
+
Some('/') if self.peek() == Some('*') => {
|
|
164
|
+
self.advance();
|
|
165
|
+
depth += 1;
|
|
166
|
+
}
|
|
152
167
|
None => return Err("Unterminated block comment".to_string()),
|
|
153
168
|
_ => {}
|
|
154
169
|
}
|
|
@@ -186,7 +201,11 @@ impl<'a> Lexer<'a> {
|
|
|
186
201
|
|
|
187
202
|
fn read_string(&mut self, quote: char) -> Result<String, String> {
|
|
188
203
|
let mut s = String::with_capacity(32);
|
|
189
|
-
let extra = if quote == '"' {
|
|
204
|
+
let extra = if quote == '"' {
|
|
205
|
+
&['"', '\''][..]
|
|
206
|
+
} else {
|
|
207
|
+
&['\'', '"'][..]
|
|
208
|
+
};
|
|
190
209
|
loop {
|
|
191
210
|
match self.advance() {
|
|
192
211
|
None => return Err("Unterminated string".to_string()),
|
|
@@ -213,24 +232,44 @@ impl<'a> Lexer<'a> {
|
|
|
213
232
|
}
|
|
214
233
|
|
|
215
234
|
/// Read a template literal. If `is_continuation` is true, we're continuing after a `}`.
|
|
216
|
-
fn read_template(
|
|
235
|
+
fn read_template(
|
|
236
|
+
&mut self,
|
|
237
|
+
start: (usize, usize),
|
|
238
|
+
is_continuation: bool,
|
|
239
|
+
) -> Result<Option<Token>, String> {
|
|
217
240
|
let mut s = String::with_capacity(if is_continuation { 32 } else { 64 });
|
|
218
241
|
let extra = &['`', '$', '{'][..];
|
|
219
|
-
|
|
242
|
+
|
|
220
243
|
loop {
|
|
221
244
|
match self.advance() {
|
|
222
245
|
None => return Err("Unterminated template literal".to_string()),
|
|
223
246
|
Some('`') => {
|
|
224
247
|
let end = self.span_start();
|
|
225
|
-
let kind = if is_continuation {
|
|
226
|
-
|
|
248
|
+
let kind = if is_continuation {
|
|
249
|
+
TokenKind::TemplateTail
|
|
250
|
+
} else {
|
|
251
|
+
TokenKind::TemplateNoSub
|
|
252
|
+
};
|
|
253
|
+
return Ok(Some(Token {
|
|
254
|
+
kind,
|
|
255
|
+
span: Span { start, end },
|
|
256
|
+
literal: Some(s.into()),
|
|
257
|
+
}));
|
|
227
258
|
}
|
|
228
259
|
Some('$') if self.peek() == Some('{') => {
|
|
229
260
|
self.advance();
|
|
230
261
|
self.template_brace_stack.push(1);
|
|
231
262
|
let end = self.span_start();
|
|
232
|
-
let kind = if is_continuation {
|
|
233
|
-
|
|
263
|
+
let kind = if is_continuation {
|
|
264
|
+
TokenKind::TemplateMiddle
|
|
265
|
+
} else {
|
|
266
|
+
TokenKind::TemplateHead
|
|
267
|
+
};
|
|
268
|
+
return Ok(Some(Token {
|
|
269
|
+
kind,
|
|
270
|
+
span: Span { start, end },
|
|
271
|
+
literal: Some(s.into()),
|
|
272
|
+
}));
|
|
234
273
|
}
|
|
235
274
|
Some('\\') => s.push(self.handle_escape(extra)?),
|
|
236
275
|
Some(c) => s.push(c),
|
|
@@ -308,7 +347,10 @@ impl<'a> Lexer<'a> {
|
|
|
308
347
|
self.indent_stack.pop();
|
|
309
348
|
return Ok(Some(Token {
|
|
310
349
|
kind: TokenKind::Dedent,
|
|
311
|
-
span: Span {
|
|
350
|
+
span: Span {
|
|
351
|
+
start: (self.line, self.col),
|
|
352
|
+
end: (self.line, self.col),
|
|
353
|
+
},
|
|
312
354
|
literal: None,
|
|
313
355
|
}));
|
|
314
356
|
}
|
|
@@ -362,31 +404,66 @@ impl<'a> Lexer<'a> {
|
|
|
362
404
|
';' => TokenKind::Semicolon,
|
|
363
405
|
',' => TokenKind::Comma,
|
|
364
406
|
'.' => {
|
|
365
|
-
if self.peek() == Some('?') {
|
|
366
|
-
|
|
407
|
+
if self.peek() == Some('?') {
|
|
408
|
+
self.advance();
|
|
409
|
+
TokenKind::OptionalChain
|
|
410
|
+
} else if self.peek() == Some('.') {
|
|
367
411
|
self.advance();
|
|
368
|
-
if self.peek() == Some('.') {
|
|
369
|
-
|
|
370
|
-
|
|
412
|
+
if self.peek() == Some('.') {
|
|
413
|
+
self.advance();
|
|
414
|
+
TokenKind::Spread
|
|
415
|
+
} else {
|
|
416
|
+
return Err("Unexpected .. (use ... for rest params)".to_string());
|
|
417
|
+
}
|
|
418
|
+
} else {
|
|
419
|
+
TokenKind::Dot
|
|
420
|
+
}
|
|
371
421
|
}
|
|
372
422
|
'=' => {
|
|
373
423
|
if self.peek() == Some('=') {
|
|
374
424
|
self.advance();
|
|
375
|
-
if self.peek() == Some('=') {
|
|
376
|
-
|
|
377
|
-
|
|
425
|
+
if self.peek() == Some('=') {
|
|
426
|
+
self.advance();
|
|
427
|
+
TokenKind::StrictEq
|
|
428
|
+
} else {
|
|
429
|
+
TokenKind::Eq
|
|
430
|
+
}
|
|
431
|
+
} else if self.peek() == Some('>') {
|
|
432
|
+
self.advance();
|
|
433
|
+
TokenKind::Arrow
|
|
434
|
+
} else {
|
|
435
|
+
TokenKind::Assign
|
|
436
|
+
}
|
|
378
437
|
}
|
|
379
438
|
'!' => {
|
|
380
439
|
if self.peek() == Some('=') {
|
|
381
440
|
self.advance();
|
|
382
|
-
if self.peek() == Some('=') {
|
|
383
|
-
|
|
441
|
+
if self.peek() == Some('=') {
|
|
442
|
+
self.advance();
|
|
443
|
+
TokenKind::StrictNe
|
|
444
|
+
} else {
|
|
445
|
+
TokenKind::Ne
|
|
446
|
+
}
|
|
447
|
+
} else {
|
|
448
|
+
TokenKind::Not
|
|
449
|
+
}
|
|
384
450
|
}
|
|
385
451
|
'<' => {
|
|
386
|
-
if self.peek() == Some('=') {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
else if self.peek() == Some('
|
|
452
|
+
if self.peek() == Some('=') {
|
|
453
|
+
self.advance();
|
|
454
|
+
TokenKind::Le
|
|
455
|
+
} else if self.peek() == Some('<') {
|
|
456
|
+
self.advance();
|
|
457
|
+
TokenKind::Shl
|
|
458
|
+
} else if self.peek() == Some('/') {
|
|
459
|
+
self.jsx_in_closing_tag = true;
|
|
460
|
+
TokenKind::Lt
|
|
461
|
+
} else if self.peek() == Some('>')
|
|
462
|
+
|| self
|
|
463
|
+
.peek()
|
|
464
|
+
.map(|c| c.is_ascii_alphabetic() || c == '_')
|
|
465
|
+
.unwrap_or(false)
|
|
466
|
+
{
|
|
390
467
|
self.jsx_depth += 1;
|
|
391
468
|
self.jsx_stack.push(JsxEl {
|
|
392
469
|
in_opener: true,
|
|
@@ -394,12 +471,18 @@ impl<'a> Lexer<'a> {
|
|
|
394
471
|
});
|
|
395
472
|
self.jsx_in_opening_tag = true;
|
|
396
473
|
TokenKind::Lt
|
|
397
|
-
} else {
|
|
474
|
+
} else {
|
|
475
|
+
TokenKind::Lt
|
|
476
|
+
}
|
|
398
477
|
}
|
|
399
478
|
'>' => {
|
|
400
|
-
if self.peek() == Some('=') {
|
|
401
|
-
|
|
402
|
-
|
|
479
|
+
if self.peek() == Some('=') {
|
|
480
|
+
self.advance();
|
|
481
|
+
TokenKind::Ge
|
|
482
|
+
} else if self.peek() == Some('>') {
|
|
483
|
+
self.advance();
|
|
484
|
+
TokenKind::Shr
|
|
485
|
+
} else {
|
|
403
486
|
if self.jsx_in_closing_tag {
|
|
404
487
|
self.jsx_depth = (self.jsx_depth - 1).max(0);
|
|
405
488
|
self.jsx_stack.pop();
|
|
@@ -425,66 +508,133 @@ impl<'a> Lexer<'a> {
|
|
|
425
508
|
'^' => TokenKind::BitXor,
|
|
426
509
|
'~' => TokenKind::BitNot,
|
|
427
510
|
'+' => {
|
|
428
|
-
if self.peek() == Some('+') {
|
|
429
|
-
|
|
430
|
-
|
|
511
|
+
if self.peek() == Some('+') {
|
|
512
|
+
self.advance();
|
|
513
|
+
TokenKind::PlusPlus
|
|
514
|
+
} else if self.peek() == Some('=') {
|
|
515
|
+
self.advance();
|
|
516
|
+
TokenKind::PlusAssign
|
|
517
|
+
} else {
|
|
518
|
+
TokenKind::Plus
|
|
519
|
+
}
|
|
431
520
|
}
|
|
432
521
|
'-' => {
|
|
433
|
-
if self.peek() == Some('-') {
|
|
434
|
-
|
|
435
|
-
|
|
522
|
+
if self.peek() == Some('-') {
|
|
523
|
+
self.advance();
|
|
524
|
+
TokenKind::MinusMinus
|
|
525
|
+
} else if self.peek() == Some('=') {
|
|
526
|
+
self.advance();
|
|
527
|
+
TokenKind::MinusAssign
|
|
528
|
+
} else {
|
|
529
|
+
TokenKind::Minus
|
|
530
|
+
}
|
|
436
531
|
}
|
|
437
532
|
'*' => {
|
|
438
|
-
if self.peek() == Some('*') {
|
|
439
|
-
|
|
440
|
-
|
|
533
|
+
if self.peek() == Some('*') {
|
|
534
|
+
self.advance();
|
|
535
|
+
TokenKind::StarStar
|
|
536
|
+
} else if self.peek() == Some('=') {
|
|
537
|
+
self.advance();
|
|
538
|
+
TokenKind::StarAssign
|
|
539
|
+
} else {
|
|
540
|
+
TokenKind::Star
|
|
541
|
+
}
|
|
441
542
|
}
|
|
442
543
|
'/' => {
|
|
443
|
-
if self.peek() == Some('/') {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
544
|
+
if self.peek() == Some('/') {
|
|
545
|
+
self.advance();
|
|
546
|
+
self.skip_line_comment();
|
|
547
|
+
// `skip_line_comment` consumes the newline via `advance()`, which sets
|
|
548
|
+
// `at_line_start` before we would normally run `skip_whitespace()`. Without
|
|
549
|
+
// stripping the next line's leading spaces here, `read_indent_level` would see
|
|
550
|
+
// physical indentation and emit a spurious `Indent` (breaks e.g. object
|
|
551
|
+
// literals with trailing `//` comments). Newlines handled in `skip_whitespace`
|
|
552
|
+
// eat those spaces before the indent pass; match that behavior.
|
|
553
|
+
self.skip_whitespace();
|
|
554
|
+
return self.next_token();
|
|
555
|
+
} else if self.peek() == Some('*') {
|
|
556
|
+
self.advance();
|
|
557
|
+
self.skip_block_comment()?;
|
|
558
|
+
return self.next_token();
|
|
559
|
+
} else if self.peek() == Some('=') {
|
|
560
|
+
self.advance();
|
|
561
|
+
TokenKind::SlashAssign
|
|
562
|
+
} else {
|
|
563
|
+
if self.jsx_in_opening_tag {
|
|
564
|
+
self.jsx_saw_slash_before_gt = true;
|
|
565
|
+
}
|
|
448
566
|
TokenKind::Slash
|
|
449
567
|
}
|
|
450
568
|
}
|
|
451
569
|
'%' => {
|
|
452
|
-
if self.peek() == Some('=') {
|
|
453
|
-
|
|
570
|
+
if self.peek() == Some('=') {
|
|
571
|
+
self.advance();
|
|
572
|
+
TokenKind::PercentAssign
|
|
573
|
+
} else {
|
|
574
|
+
TokenKind::Percent
|
|
575
|
+
}
|
|
454
576
|
}
|
|
455
577
|
'&' => {
|
|
456
578
|
if self.peek() == Some('&') {
|
|
457
579
|
self.advance();
|
|
458
|
-
if self.peek() == Some('=') {
|
|
459
|
-
|
|
460
|
-
|
|
580
|
+
if self.peek() == Some('=') {
|
|
581
|
+
self.advance();
|
|
582
|
+
TokenKind::AndAndAssign
|
|
583
|
+
} else {
|
|
584
|
+
TokenKind::And
|
|
585
|
+
}
|
|
586
|
+
} else {
|
|
587
|
+
TokenKind::BitAnd
|
|
588
|
+
}
|
|
461
589
|
}
|
|
462
590
|
'|' => {
|
|
463
591
|
if self.peek() == Some('|') {
|
|
464
592
|
self.advance();
|
|
465
|
-
if self.peek() == Some('=') {
|
|
466
|
-
|
|
467
|
-
|
|
593
|
+
if self.peek() == Some('=') {
|
|
594
|
+
self.advance();
|
|
595
|
+
TokenKind::OrOrAssign
|
|
596
|
+
} else {
|
|
597
|
+
TokenKind::Or
|
|
598
|
+
}
|
|
599
|
+
} else {
|
|
600
|
+
TokenKind::BitOr
|
|
601
|
+
}
|
|
468
602
|
}
|
|
469
603
|
'?' => {
|
|
470
604
|
if self.peek() == Some('?') {
|
|
471
605
|
self.advance();
|
|
472
|
-
if self.peek() == Some('=') {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
606
|
+
if self.peek() == Some('=') {
|
|
607
|
+
self.advance();
|
|
608
|
+
TokenKind::NullishAssign
|
|
609
|
+
} else {
|
|
610
|
+
TokenKind::NullishCoalesce
|
|
611
|
+
}
|
|
612
|
+
} else if self.peek() == Some('.') {
|
|
613
|
+
self.advance();
|
|
614
|
+
TokenKind::OptionalChain
|
|
615
|
+
} else {
|
|
616
|
+
TokenKind::Question
|
|
617
|
+
}
|
|
476
618
|
}
|
|
477
619
|
':' => TokenKind::Colon,
|
|
478
620
|
'"' | '\'' => {
|
|
479
621
|
let s = self.read_string(c)?;
|
|
480
622
|
let end = self.span_start();
|
|
481
|
-
return Ok(Some(Token {
|
|
623
|
+
return Ok(Some(Token {
|
|
624
|
+
kind: TokenKind::String,
|
|
625
|
+
span: Span { start, end },
|
|
626
|
+
literal: Some(s.into()),
|
|
627
|
+
}));
|
|
482
628
|
}
|
|
483
629
|
'`' => return self.read_template(start, false),
|
|
484
630
|
'0'..='9' => {
|
|
485
631
|
let num = self.read_number(c);
|
|
486
632
|
let end = self.span_start();
|
|
487
|
-
return Ok(Some(Token {
|
|
633
|
+
return Ok(Some(Token {
|
|
634
|
+
kind: TokenKind::Number,
|
|
635
|
+
span: Span { start, end },
|
|
636
|
+
literal: Some(num.into()),
|
|
637
|
+
}));
|
|
488
638
|
}
|
|
489
639
|
'a'..='z' | 'A'..='Z' | '_' => {
|
|
490
640
|
let ident = self.read_ident_or_keyword(c);
|
|
@@ -493,15 +643,23 @@ impl<'a> Lexer<'a> {
|
|
|
493
643
|
return Ok(Some(Token {
|
|
494
644
|
kind,
|
|
495
645
|
span: Span { start, end },
|
|
496
|
-
|
|
646
|
+
// Spelling is useful for keywords too (e.g. object keys, type names like `type`).
|
|
647
|
+
literal: Some(ident.into()),
|
|
497
648
|
}));
|
|
498
649
|
}
|
|
499
|
-
'\n' => {
|
|
650
|
+
'\n' => {
|
|
651
|
+
self.at_line_start = true;
|
|
652
|
+
return self.next_token();
|
|
653
|
+
}
|
|
500
654
|
_ => return Err(format!("Unexpected character: {:?}", c)),
|
|
501
655
|
};
|
|
502
656
|
|
|
503
657
|
let end = self.span_start();
|
|
504
|
-
Ok(Some(Token {
|
|
658
|
+
Ok(Some(Token {
|
|
659
|
+
kind,
|
|
660
|
+
span: Span { start, end },
|
|
661
|
+
literal: None,
|
|
662
|
+
}))
|
|
505
663
|
}
|
|
506
664
|
}
|
|
507
665
|
|
|
@@ -539,4 +697,15 @@ mod tests {
|
|
|
539
697
|
let string_tok = tokens.iter().find(|t| t.kind == TokenKind::String).unwrap();
|
|
540
698
|
assert_eq!(string_tok.literal.as_deref(), Some("H"));
|
|
541
699
|
}
|
|
700
|
+
|
|
701
|
+
#[test]
|
|
702
|
+
fn line_comment_does_not_emit_spurious_indent_before_next_line() {
|
|
703
|
+
let with_comment = "fn f() {\n return {\n a: 1, // c\n b: 2\n }\n}\n";
|
|
704
|
+
let tokens: Vec<_> = Lexer::new(with_comment).collect::<Result<Vec<_>, _>>().unwrap();
|
|
705
|
+
assert!(
|
|
706
|
+
!tokens.iter().any(|t| t.kind == TokenKind::Indent),
|
|
707
|
+
"unexpected Indent after line comment: {:?}",
|
|
708
|
+
tokens.iter().map(|t| format!("{:?}", t.kind)).collect::<Vec<_>>()
|
|
709
|
+
);
|
|
710
|
+
}
|
|
542
711
|
}
|
|
@@ -57,6 +57,8 @@ pub enum TokenKind {
|
|
|
57
57
|
New,
|
|
58
58
|
Import,
|
|
59
59
|
Export,
|
|
60
|
+
Type,
|
|
61
|
+
Declare,
|
|
60
62
|
|
|
61
63
|
// Punctuation
|
|
62
64
|
LParen,
|
|
@@ -110,14 +112,14 @@ pub enum TokenKind {
|
|
|
110
112
|
NullishCoalesce,
|
|
111
113
|
Question,
|
|
112
114
|
Arrow,
|
|
113
|
-
|
|
115
|
+
|
|
114
116
|
// Template literal tokens
|
|
115
|
-
TemplateNoSub,
|
|
116
|
-
TemplateHead,
|
|
117
|
-
TemplateMiddle,
|
|
118
|
-
TemplateTail,
|
|
117
|
+
TemplateNoSub, // `text` (no interpolation)
|
|
118
|
+
TemplateHead, // `text${ (start with interpolation)
|
|
119
|
+
TemplateMiddle, // }text${ (middle part)
|
|
120
|
+
TemplateTail, // }text` (end part)
|
|
119
121
|
|
|
120
|
-
JsxText,
|
|
122
|
+
JsxText, // Raw text in JSX children (emojis, etc.); only {}<> are special
|
|
121
123
|
}
|
|
122
124
|
|
|
123
125
|
impl TokenKind {
|
|
@@ -153,6 +155,8 @@ impl TokenKind {
|
|
|
153
155
|
"new" => TokenKind::New,
|
|
154
156
|
"import" => TokenKind::Import,
|
|
155
157
|
"export" => TokenKind::Export,
|
|
158
|
+
"type" => TokenKind::Type,
|
|
159
|
+
"declare" => TokenKind::Declare,
|
|
156
160
|
_ => TokenKind::Ident,
|
|
157
161
|
}
|
|
158
162
|
}
|
|
@@ -17,10 +17,17 @@ pub fn compile_to_native(
|
|
|
17
17
|
project_root: Option<&Path>,
|
|
18
18
|
output_path: &Path,
|
|
19
19
|
) -> Result<(), LlvmError> {
|
|
20
|
-
let modules = resolve_project(entry_path, project_root)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
let modules = resolve_project(entry_path, project_root).map_err(|e| LlvmError {
|
|
21
|
+
message: e.to_string(),
|
|
22
|
+
})?;
|
|
23
|
+
detect_cycles(&modules).map_err(|e| LlvmError {
|
|
24
|
+
message: e.to_string(),
|
|
25
|
+
})?;
|
|
26
|
+
let program = merge_modules(modules)
|
|
27
|
+
.map(|m| m.program)
|
|
28
|
+
.map_err(|e| LlvmError {
|
|
29
|
+
message: e.to_string(),
|
|
30
|
+
})?;
|
|
24
31
|
let chunk = tishlang_bytecode::compile(&program).map_err(|e| LlvmError {
|
|
25
32
|
message: e.to_string(),
|
|
26
33
|
})?;
|
|
@@ -71,7 +78,10 @@ const uint64_t tish_chunk_len = sizeof(tish_chunk_data);
|
|
|
71
78
|
.arg(&chunk_c_path)
|
|
72
79
|
.status()
|
|
73
80
|
.map_err(|e| LlvmError {
|
|
74
|
-
message: format!(
|
|
81
|
+
message: format!(
|
|
82
|
+
"Failed to run clang: {}. Install clang (LLVM) or set CLANG env var.",
|
|
83
|
+
e
|
|
84
|
+
),
|
|
75
85
|
})?;
|
|
76
86
|
|
|
77
87
|
if !status.success() {
|
|
@@ -84,9 +94,8 @@ const uint64_t tish_chunk_len = sizeof(tish_chunk_data);
|
|
|
84
94
|
let object_path_canonical = object_path.canonicalize().map_err(|e| LlvmError {
|
|
85
95
|
message: format!("Cannot canonicalize object path: {}", e),
|
|
86
96
|
})?;
|
|
87
|
-
tishlang_cranelift::link_to_binary(&object_path_canonical, output_path, features)
|
|
88
|
-
message: e.message
|
|
89
|
-
})?;
|
|
97
|
+
tishlang_cranelift::link_to_binary(&object_path_canonical, output_path, features)
|
|
98
|
+
.map_err(|e| LlvmError { message: e.message })?;
|
|
90
99
|
|
|
91
100
|
Ok(())
|
|
92
101
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "tishlang_lsp"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.1"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
description = "Language Server Protocol implementation for Tish"
|
|
6
6
|
license-file = { workspace = true }
|
|
@@ -12,9 +12,12 @@ path = "src/main.rs"
|
|
|
12
12
|
|
|
13
13
|
[dependencies]
|
|
14
14
|
tishlang_ast = { path = "../tish_ast", version = ">=0.1" }
|
|
15
|
+
tishlang_cargo_bindgen = { path = "../tishlang_cargo_bindgen", version = ">=0.1" }
|
|
16
|
+
tishlang_compile = { path = "../tish_compile", version = ">=0.1" }
|
|
15
17
|
tishlang_parser = { path = "../tish_parser", version = ">=0.1" }
|
|
16
18
|
tishlang_fmt = { path = "../tish_fmt", version = ">=0.1" }
|
|
17
19
|
tishlang_lint = { path = "../tish_lint", version = ">=0.1" }
|
|
20
|
+
tishlang_resolve = { path = "../tish_resolve", version = ">=0.1" }
|
|
18
21
|
tower-lsp = "0.20"
|
|
19
22
|
tokio = { version = "1", features = ["rt-multi-thread", "macros", "io-std"] }
|
|
20
23
|
serde_json = "1"
|
|
@@ -14,7 +14,7 @@ Binary: `target/release/tish-lsp` (stdio LSP).
|
|
|
14
14
|
|
|
15
15
|
- Parse diagnostics + lint warnings (via `tish_lint` **library** — use **`tish-lint`** CLI separately in CI)
|
|
16
16
|
- Document symbols, completion, formatting (via `tish_fmt` **library** — use **`tish-fmt`** CLI separately in CI)
|
|
17
|
-
- Go to definition (same file +
|
|
17
|
+
- Go to definition (same file, relative `./` / `../`, bare `node_modules` packages like Node, and native `tish:` / `@scope/pkg` / `cargo:` → Rust `pub fn` via `syn` + `cargo metadata` where configured)
|
|
18
18
|
- Workspace symbol search (`**/*.tish`)
|
|
19
19
|
|
|
20
20
|
## Client configuration
|