tree-sitter-sh 0.15.0 → 0.16.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 +8 -20
- package/README.md +11 -2
- package/bindings/rust/build.rs +20 -6
- package/bindings/rust/lib.rs +3 -47
- package/grammar.js +69 -83
- package/package.json +8 -3
- package/src/arithmetic.h +27 -47
- package/src/grammar.json +760 -767
- package/src/input.h +0 -1
- package/src/parser.c +2 -2
- package/src/scanner.c +284 -621
- package/src/source.h +31 -12
- package/test/bindings.test.rs +42 -0
- package/tree-sitter.json +5 -10
package/Cargo.toml
CHANGED
|
@@ -1,30 +1,18 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "tree-sitter-sh"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.16.0"
|
|
4
4
|
edition = "2024"
|
|
5
|
-
description = "Tree-sitter grammar for POSIX sh."
|
|
6
|
-
readme = "README.md"
|
|
7
|
-
repository = "https://github.com/konomanoasa/tree-sitter-sh"
|
|
8
|
-
license = "MIT"
|
|
9
|
-
keywords = ["incremental", "parsing", "sh", "tree-sitter"]
|
|
10
|
-
categories = ["parser-implementations", "parsing", "text-editors"]
|
|
11
5
|
build = "bindings/rust/build.rs"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"/README.md",
|
|
15
|
-
"/bindings/rust/*.rs",
|
|
16
|
-
"/grammar.js",
|
|
17
|
-
"/queries/*.scm",
|
|
18
|
-
"/src/*.c",
|
|
19
|
-
"/src/*.h",
|
|
20
|
-
"/src/*.json",
|
|
21
|
-
"/src/tree_sitter/*.h",
|
|
22
|
-
"/tree-sitter.json",
|
|
23
|
-
]
|
|
6
|
+
links = "tree-sitter-sh"
|
|
7
|
+
publish = false
|
|
24
8
|
|
|
25
9
|
[lib]
|
|
26
10
|
path = "bindings/rust/lib.rs"
|
|
27
11
|
|
|
12
|
+
[[test]]
|
|
13
|
+
name = "bindings"
|
|
14
|
+
path = "test/bindings.test.rs"
|
|
15
|
+
|
|
28
16
|
[dependencies]
|
|
29
17
|
tree-sitter-language = "0.1.8"
|
|
30
18
|
|
|
@@ -32,4 +20,4 @@ tree-sitter-language = "0.1.8"
|
|
|
32
20
|
tree-sitter = "0.27.0"
|
|
33
21
|
|
|
34
22
|
[build-dependencies]
|
|
35
|
-
cc = "1.4
|
|
23
|
+
cc = "1.4"
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# tree-sitter-sh
|
|
2
2
|
|
|
3
|
-
[](https://github.com/konomanoasa/tree-sitter-sh/actions/workflows/ci.yaml)
|
|
4
4
|
[](https://www.npmjs.com/package/tree-sitter-sh)
|
|
5
5
|
|
|
6
6
|
[Tree-sitter](https://tree-sitter.github.io/tree-sitter/) grammar for the
|
|
@@ -12,11 +12,20 @@ POSIX.1-2024 Shell Command Language.
|
|
|
12
12
|
npm install tree-sitter-sh
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
## Grammar
|
|
16
|
+
|
|
17
|
+
| Grammar | Description | Rust constant |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| `sh` | POSIX.1-2024 Shell Command Language | `LANGUAGE` |
|
|
20
|
+
|
|
15
21
|
## Development
|
|
16
22
|
|
|
23
|
+
Development requires Node.js 24.2.0 or later.
|
|
24
|
+
|
|
17
25
|
```sh
|
|
18
26
|
npm install
|
|
19
|
-
npm run
|
|
27
|
+
npm run build
|
|
28
|
+
npm test
|
|
20
29
|
```
|
|
21
30
|
|
|
22
31
|
## Specification
|
package/bindings/rust/build.rs
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
|
+
use std::{env, path::Path};
|
|
2
|
+
|
|
1
3
|
fn main() {
|
|
2
|
-
|
|
4
|
+
let mut build = cc::Build::new();
|
|
5
|
+
build.std("c17");
|
|
6
|
+
if build.get_compiler().is_like_msvc() {
|
|
7
|
+
build.flag("-utf-8");
|
|
8
|
+
}
|
|
9
|
+
if env::var("TARGET").expect("Cargo must provide TARGET")
|
|
10
|
+
== "wasm32-unknown-unknown"
|
|
11
|
+
{
|
|
12
|
+
let headers = env::var_os("DEP_TREE_SITTER_LANGUAGE_WASM_HEADERS")
|
|
13
|
+
.expect("tree-sitter-language must provide WebAssembly headers");
|
|
14
|
+
build.include(&headers);
|
|
15
|
+
println!("cargo::rerun-if-changed={}", Path::new(&headers).display());
|
|
16
|
+
}
|
|
3
17
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
18
|
+
println!("cargo::rerun-if-changed=src");
|
|
19
|
+
build
|
|
20
|
+
.include("src")
|
|
21
|
+
.files(["src/parser.c", "src/scanner.c"])
|
|
22
|
+
.compile("tree-sitter-sh");
|
|
9
23
|
}
|
package/bindings/rust/lib.rs
CHANGED
|
@@ -1,56 +1,12 @@
|
|
|
1
|
-
//! POSIX.1-2024 Shell Command Language support for Tree-sitter.
|
|
2
|
-
//!
|
|
3
|
-
//! ```
|
|
4
|
-
//! let mut parser = tree_sitter::Parser::new();
|
|
5
|
-
//! parser.set_language(&tree_sitter_sh::LANGUAGE.into()).unwrap();
|
|
6
|
-
//! let tree = parser.parse("echo hello\n", None).unwrap();
|
|
7
|
-
//! assert!(!tree.root_node().has_error());
|
|
8
|
-
//! ```
|
|
9
|
-
|
|
10
1
|
use tree_sitter_language::LanguageFn;
|
|
11
2
|
|
|
12
3
|
unsafe extern "C" {
|
|
13
|
-
|
|
4
|
+
fn tree_sitter_sh() -> *const ();
|
|
14
5
|
}
|
|
15
6
|
|
|
16
|
-
|
|
17
|
-
|
|
7
|
+
pub const LANGUAGE: LanguageFn =
|
|
8
|
+
unsafe { LanguageFn::from_raw(tree_sitter_sh) };
|
|
18
9
|
|
|
19
|
-
/// Public node types and fields in Tree-sitter's JSON format.
|
|
20
10
|
pub const NODE_TYPES: &str = include_str!("../../src/node-types.json");
|
|
21
11
|
|
|
22
|
-
/// Highlight query using standard Tree-sitter and Neovim captures.
|
|
23
12
|
pub const HIGHLIGHTS_QUERY: &str = include_str!("../../queries/highlights.scm");
|
|
24
|
-
|
|
25
|
-
#[cfg(test)]
|
|
26
|
-
mod tests {
|
|
27
|
-
use tree_sitter::{Parser, Query, QueryCursor, StreamingIterator};
|
|
28
|
-
|
|
29
|
-
#[test]
|
|
30
|
-
fn parses_quoted_here_document_and_highlights_its_terminator() {
|
|
31
|
-
let source = "cat <<'EOF'\n$HOME\nEOF\n";
|
|
32
|
-
let language = super::LANGUAGE.into();
|
|
33
|
-
let mut parser = Parser::new();
|
|
34
|
-
parser.set_language(&language).unwrap();
|
|
35
|
-
let tree = parser.parse(source, None).unwrap();
|
|
36
|
-
let root = tree.root_node();
|
|
37
|
-
assert_eq!(root.kind(), "program");
|
|
38
|
-
assert_eq!(root.byte_range(), 0..source.len());
|
|
39
|
-
assert!(!root.has_error(), "{}", root.to_sexp());
|
|
40
|
-
|
|
41
|
-
let query = Query::new(&language, super::HIGHLIGHTS_QUERY).unwrap();
|
|
42
|
-
let mut cursor = QueryCursor::new();
|
|
43
|
-
let mut captures = cursor.captures(&query, root, source.as_bytes());
|
|
44
|
-
let mut terminators = 0;
|
|
45
|
-
while let Some((matched, index)) = captures.next() {
|
|
46
|
-
let capture = matched.captures()[*index];
|
|
47
|
-
if capture.node.kind() == "here_document_end_text" {
|
|
48
|
-
assert_eq!(query.capture_names()[capture.index as usize], "label");
|
|
49
|
-
assert_eq!(capture.node.byte_range(), 18..21);
|
|
50
|
-
assert_eq!(capture.node.utf8_text(source.as_bytes()).unwrap(), "EOF");
|
|
51
|
-
terminators += 1;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
assert_eq!(terminators, 1);
|
|
55
|
-
}
|
|
56
|
-
}
|
package/grammar.js
CHANGED
|
@@ -76,16 +76,50 @@ const lexical = ($, begin) =>
|
|
|
76
76
|
const wholeLexical = ($, begin, decorate = (source) => source) =>
|
|
77
77
|
seq(optional(begin), decorate(sourceToken($, begin, "lexical", "whole")));
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
// Alias the token directly so inserted MISSING tokens stay visible.
|
|
80
|
+
const wholeSource = ($, begin, lexeme, visible) =>
|
|
80
81
|
choice(
|
|
81
|
-
wholeLexical($,
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
wholeLexical($, begin, (source) => alias(source, visible)),
|
|
83
|
+
alias(lexeme, visible),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const KEYWORDS = [
|
|
87
|
+
"if",
|
|
88
|
+
"then",
|
|
89
|
+
"elif",
|
|
90
|
+
"else",
|
|
91
|
+
"fi",
|
|
92
|
+
"for",
|
|
93
|
+
"in",
|
|
94
|
+
"do",
|
|
95
|
+
"done",
|
|
96
|
+
"case",
|
|
97
|
+
"esac",
|
|
98
|
+
"while",
|
|
99
|
+
"until",
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
const keywordRules = () =>
|
|
103
|
+
Object.fromEntries(
|
|
104
|
+
KEYWORDS.flatMap((name) => [
|
|
105
|
+
[
|
|
106
|
+
`_${name}_keyword_lexeme`,
|
|
107
|
+
($) => lexical($, $[`_${name}_keyword_begin`]),
|
|
108
|
+
],
|
|
109
|
+
[
|
|
110
|
+
`_${name}_keyword_source`,
|
|
111
|
+
($) =>
|
|
112
|
+
wholeSource(
|
|
113
|
+
$,
|
|
114
|
+
$[`_${name}_keyword_begin`],
|
|
115
|
+
$[`_${name}_keyword_lexeme`],
|
|
116
|
+
$[`${name}_keyword`],
|
|
117
|
+
),
|
|
118
|
+
],
|
|
119
|
+
]),
|
|
85
120
|
);
|
|
86
121
|
|
|
87
|
-
//
|
|
88
|
-
// error recovery can still reach the enclosing command.
|
|
122
|
+
// Reduce each delimiter to one stack entry so recovery can reach the command.
|
|
89
123
|
const PHYSICAL_RULES = {
|
|
90
124
|
_punct_left_parenthesis: "(",
|
|
91
125
|
_punct_right_parenthesis: ")",
|
|
@@ -488,7 +522,7 @@ const parenthesizedArithmetic = ($, expression) =>
|
|
|
488
522
|
const arithmeticExpansionStart = (
|
|
489
523
|
$,
|
|
490
524
|
marker,
|
|
491
|
-
start = $.
|
|
525
|
+
start = $._command_substitution_start,
|
|
492
526
|
) => seq(start, marker, physical($, $._punct_left_parenthesis, "("));
|
|
493
527
|
const closedArithmeticExpansion = ($, start, expression, closing) =>
|
|
494
528
|
seq(
|
|
@@ -569,6 +603,8 @@ const dollarSingleQuoted = ($, dollar) =>
|
|
|
569
603
|
repeat(choice($.dollar_single_quote_text, $.dollar_single_quote_escape)),
|
|
570
604
|
physical($, $._dollar_sq_close, "'"),
|
|
571
605
|
);
|
|
606
|
+
const hereDocumentEndSource = ($, begin, text) =>
|
|
607
|
+
seq(begin, repeat(choice($._here_document_end_leading_tabs, text)));
|
|
572
608
|
const backquoteSubstitution = ($, start) =>
|
|
573
609
|
seq(
|
|
574
610
|
backquoteDelimiter($, start),
|
|
@@ -743,8 +779,7 @@ const conditionalThenBranch = ($, header, tail) =>
|
|
|
743
779
|
tail,
|
|
744
780
|
);
|
|
745
781
|
|
|
746
|
-
//
|
|
747
|
-
// lets elif reduce its empty alternative before reaching else.
|
|
782
|
+
// Layout before fi lets elif reduce its empty alternative before else.
|
|
748
783
|
const ifClause = ($) =>
|
|
749
784
|
conditionalThenBranch(
|
|
750
785
|
$,
|
|
@@ -1159,31 +1194,7 @@ export default grammar({
|
|
|
1159
1194
|
|
|
1160
1195
|
_or_if_lexeme: ($) => lexical($, $._or_if_begin),
|
|
1161
1196
|
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
_then_keyword_lexeme: ($) => lexical($, $._then_keyword_begin),
|
|
1165
|
-
|
|
1166
|
-
_elif_keyword_lexeme: ($) => lexical($, $._elif_keyword_begin),
|
|
1167
|
-
|
|
1168
|
-
_else_keyword_lexeme: ($) => lexical($, $._else_keyword_begin),
|
|
1169
|
-
|
|
1170
|
-
_fi_keyword_lexeme: ($) => lexical($, $._fi_keyword_begin),
|
|
1171
|
-
|
|
1172
|
-
_for_keyword_lexeme: ($) => lexical($, $._for_keyword_begin),
|
|
1173
|
-
|
|
1174
|
-
_in_keyword_lexeme: ($) => lexical($, $._in_keyword_begin),
|
|
1175
|
-
|
|
1176
|
-
_do_keyword_lexeme: ($) => lexical($, $._do_keyword_begin),
|
|
1177
|
-
|
|
1178
|
-
_done_keyword_lexeme: ($) => lexical($, $._done_keyword_begin),
|
|
1179
|
-
|
|
1180
|
-
_case_keyword_lexeme: ($) => lexical($, $._case_keyword_begin),
|
|
1181
|
-
|
|
1182
|
-
_esac_keyword_lexeme: ($) => lexical($, $._esac_keyword_begin),
|
|
1183
|
-
|
|
1184
|
-
_while_keyword_lexeme: ($) => lexical($, $._while_keyword_begin),
|
|
1185
|
-
|
|
1186
|
-
_until_keyword_lexeme: ($) => lexical($, $._until_keyword_begin),
|
|
1197
|
+
...keywordRules(),
|
|
1187
1198
|
|
|
1188
1199
|
_dsemi_lexeme: ($) => lexical($, $._dsemi_begin),
|
|
1189
1200
|
|
|
@@ -1407,19 +1418,6 @@ export default grammar({
|
|
|
1407
1418
|
_and_if: ($) => alias($._and_if_lexeme, $.and_if),
|
|
1408
1419
|
_or_if: ($) => alias($._or_if_lexeme, $.or_if),
|
|
1409
1420
|
bang: ($) => lexical($, $._bang_begin),
|
|
1410
|
-
_if_keyword_source: ($) => keywordSource($, "if"),
|
|
1411
|
-
_then_keyword_source: ($) => keywordSource($, "then"),
|
|
1412
|
-
_elif_keyword_source: ($) => keywordSource($, "elif"),
|
|
1413
|
-
_else_keyword_source: ($) => keywordSource($, "else"),
|
|
1414
|
-
_fi_keyword_source: ($) => keywordSource($, "fi"),
|
|
1415
|
-
_for_keyword_source: ($) => keywordSource($, "for"),
|
|
1416
|
-
_in_keyword_source: ($) => keywordSource($, "in"),
|
|
1417
|
-
_do_keyword_source: ($) => keywordSource($, "do"),
|
|
1418
|
-
_done_keyword_source: ($) => keywordSource($, "done"),
|
|
1419
|
-
_case_keyword_source: ($) => keywordSource($, "case"),
|
|
1420
|
-
_esac_keyword_source: ($) => keywordSource($, "esac"),
|
|
1421
|
-
_while_keyword_source: ($) => keywordSource($, "while"),
|
|
1422
|
-
_until_keyword_source: ($) => keywordSource($, "until"),
|
|
1423
1421
|
function_definition: ($) =>
|
|
1424
1422
|
seq(
|
|
1425
1423
|
$._function_definition_header,
|
|
@@ -1429,8 +1427,6 @@ export default grammar({
|
|
|
1429
1427
|
field("body", $.function_body),
|
|
1430
1428
|
),
|
|
1431
1429
|
|
|
1432
|
-
// The header reduces before the body so its pieces do not stay on the
|
|
1433
|
-
// stack while the body is parsed.
|
|
1434
1430
|
_function_definition_header: ($) =>
|
|
1435
1431
|
seq(
|
|
1436
1432
|
field("name", $.fname),
|
|
@@ -1724,26 +1720,18 @@ export default grammar({
|
|
|
1724
1720
|
here_document_end: ($) =>
|
|
1725
1721
|
seq(
|
|
1726
1722
|
choice(
|
|
1727
|
-
|
|
1723
|
+
hereDocumentEndSource(
|
|
1724
|
+
$,
|
|
1728
1725
|
$._quoted_here_document_end_begin,
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
alias(
|
|
1733
|
-
$._quoted_here_document_end_text_lexeme,
|
|
1734
|
-
$.here_document_end_text,
|
|
1735
|
-
),
|
|
1736
|
-
),
|
|
1726
|
+
alias(
|
|
1727
|
+
$._quoted_here_document_end_text_lexeme,
|
|
1728
|
+
$.here_document_end_text,
|
|
1737
1729
|
),
|
|
1738
1730
|
),
|
|
1739
|
-
|
|
1731
|
+
hereDocumentEndSource(
|
|
1732
|
+
$,
|
|
1740
1733
|
$._here_document_end_begin,
|
|
1741
|
-
|
|
1742
|
-
choice(
|
|
1743
|
-
$._here_document_end_leading_tabs,
|
|
1744
|
-
$.here_document_end_text,
|
|
1745
|
-
),
|
|
1746
|
-
),
|
|
1734
|
+
$.here_document_end_text,
|
|
1747
1735
|
),
|
|
1748
1736
|
),
|
|
1749
1737
|
optional($._here_document_end_line_end),
|
|
@@ -1800,7 +1788,7 @@ export default grammar({
|
|
|
1800
1788
|
),
|
|
1801
1789
|
),
|
|
1802
1790
|
|
|
1803
|
-
|
|
1791
|
+
_assignment_word_part: ($) =>
|
|
1804
1792
|
choice(
|
|
1805
1793
|
$._assignment_bracket_fallback,
|
|
1806
1794
|
$.pattern_bracket_source,
|
|
@@ -1809,7 +1797,6 @@ export default grammar({
|
|
|
1809
1797
|
$.pattern_question_source,
|
|
1810
1798
|
$._word_structured_part,
|
|
1811
1799
|
),
|
|
1812
|
-
_assignment_word_part: ($) => $._assignment_non_delimiter_part,
|
|
1813
1800
|
|
|
1814
1801
|
_assignment_literal: ($) => prec.right(assignmentPlainChunk($)),
|
|
1815
1802
|
|
|
@@ -1838,10 +1825,10 @@ export default grammar({
|
|
|
1838
1825
|
$._word_tilde_end,
|
|
1839
1826
|
),
|
|
1840
1827
|
|
|
1841
|
-
tilde_user: ($) => repeat1($.
|
|
1842
|
-
_assignment_tilde_user: ($) => repeat1($.
|
|
1843
|
-
_parameter_tilde_user: ($) => repeat1($.
|
|
1844
|
-
|
|
1828
|
+
tilde_user: ($) => repeat1($._word_part),
|
|
1829
|
+
_assignment_tilde_user: ($) => repeat1($._assignment_word_part),
|
|
1830
|
+
_parameter_tilde_user: ($) => repeat1($._parameter_pattern_part),
|
|
1831
|
+
_word_part: ($) =>
|
|
1845
1832
|
choice(
|
|
1846
1833
|
$._word_bracket_fallback,
|
|
1847
1834
|
$.pattern_bracket_source,
|
|
@@ -1851,14 +1838,13 @@ export default grammar({
|
|
|
1851
1838
|
$._word_structured_part,
|
|
1852
1839
|
),
|
|
1853
1840
|
|
|
1854
|
-
_word_part: ($) => $._word_non_slash_part,
|
|
1855
1841
|
_word_structured_part: ($) => choice(...structuredSourceParts($)),
|
|
1856
1842
|
|
|
1857
1843
|
_word_initial_part: ($) =>
|
|
1858
1844
|
choice(
|
|
1859
1845
|
$._word_initial_bracket_fallback,
|
|
1860
1846
|
alias($._word_initial_pattern_bracket, $.pattern_bracket_source),
|
|
1861
|
-
|
|
1847
|
+
$._word_initial_literal,
|
|
1862
1848
|
alias($._word_initial_pattern_star, $.pattern_star_source),
|
|
1863
1849
|
alias($._word_initial_pattern_question, $.pattern_question_source),
|
|
1864
1850
|
alias($._word_initial_escaped_character, $.escaped_character),
|
|
@@ -1871,10 +1857,14 @@ export default grammar({
|
|
|
1871
1857
|
alias($._word_initial_backquote_substitution, $.backquote_substitution),
|
|
1872
1858
|
),
|
|
1873
1859
|
_word_initial_literal: ($) =>
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1860
|
+
wholeSource(
|
|
1861
|
+
$,
|
|
1862
|
+
$._word_initial_literal_begin,
|
|
1863
|
+
$._word_initial_literal_lexeme,
|
|
1864
|
+
$.literal,
|
|
1877
1865
|
),
|
|
1866
|
+
_word_initial_literal_lexeme: ($) =>
|
|
1867
|
+
lexical($, $._word_initial_literal_begin),
|
|
1878
1868
|
_word_initial_pattern_star: ($) =>
|
|
1879
1869
|
lexical($, $._word_initial_pattern_star_begin),
|
|
1880
1870
|
_word_initial_pattern_question: ($) =>
|
|
@@ -2256,7 +2246,7 @@ export default grammar({
|
|
|
2256
2246
|
|
|
2257
2247
|
_parameter_tilde_source: ($) =>
|
|
2258
2248
|
alias($._parameter_tilde_expansion, $.tilde_expansion),
|
|
2259
|
-
|
|
2249
|
+
_parameter_pattern_part: ($) =>
|
|
2260
2250
|
choice(
|
|
2261
2251
|
$._parameter_bracket_fallback,
|
|
2262
2252
|
alias(
|
|
@@ -2268,8 +2258,6 @@ export default grammar({
|
|
|
2268
2258
|
$.pattern_question_source,
|
|
2269
2259
|
$._word_structured_part,
|
|
2270
2260
|
),
|
|
2271
|
-
_parameter_pattern_part: ($) => $._parameter_non_slash_part,
|
|
2272
|
-
|
|
2273
2261
|
_parameter_pattern_literal: ($) => prec.right(parameterPlainChunk($)),
|
|
2274
2262
|
|
|
2275
2263
|
_double_quoted_parameter_text: ($) =>
|
|
@@ -2278,12 +2266,10 @@ export default grammar({
|
|
|
2278
2266
|
_double_quoted_parameter_escape: ($) =>
|
|
2279
2267
|
lexical($, $._double_quoted_parameter_escape_begin),
|
|
2280
2268
|
command_substitution: ($) =>
|
|
2281
|
-
commandSubstitution($, $.
|
|
2269
|
+
commandSubstitution($, $._command_substitution_start),
|
|
2282
2270
|
|
|
2283
2271
|
_command_substitution_start: ($) =>
|
|
2284
2272
|
dollarExpansionStart($, $._command_open, "("),
|
|
2285
|
-
_command_or_arithmetic_substitution_start: ($) =>
|
|
2286
|
-
$._command_substitution_start,
|
|
2287
2273
|
command_substitution_body: ($) =>
|
|
2288
2274
|
prec.left(substitutionCommandsBody($, $._closing_layout)),
|
|
2289
2275
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tree-sitter-sh",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Tree-sitter grammar for POSIX sh.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -14,14 +14,19 @@
|
|
|
14
14
|
"src/*.h",
|
|
15
15
|
"src/*.json",
|
|
16
16
|
"src/tree_sitter/*.h",
|
|
17
|
+
"test/bindings.test.rs",
|
|
17
18
|
"tree-sitter.json"
|
|
18
19
|
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
19
23
|
"scripts": {
|
|
24
|
+
"build": "npm run tree-sitter -- build-all",
|
|
20
25
|
"check": "biome check . && node scripts/check-c.js && node scripts/check-generated.js && npm test",
|
|
21
26
|
"format": "biome check --write . && node scripts/check-c.js --write",
|
|
22
27
|
"generate": "npm run tree-sitter -- generate-all",
|
|
23
|
-
"parse": "npm run tree-sitter -- parse --scope source.sh",
|
|
24
28
|
"test": "npm run tree-sitter -- test-corpus --rebuild && node --test --test-concurrency=1 \"test/*.test.js\"",
|
|
29
|
+
"test:bindings": "cargo test --locked",
|
|
25
30
|
"test:fuzz": "node --test --test-name-pattern=\"fixed-seed generated histories\" test/incremental.test.js",
|
|
26
31
|
"test:fuzz:corpus": "npm run tree-sitter -- fuzz-all --iterations 100 --edits 5",
|
|
27
32
|
"test:highlights": "node --test test/highlight.test.js",
|
|
@@ -32,7 +37,7 @@
|
|
|
32
37
|
"tree-sitter": "node scripts/tree-sitter.js"
|
|
33
38
|
},
|
|
34
39
|
"devDependencies": {
|
|
35
|
-
"@biomejs/biome": "2.5.
|
|
40
|
+
"@biomejs/biome": "2.5.14",
|
|
36
41
|
"@types/node": "26.5.1",
|
|
37
42
|
"tree-sitter-cli": "0.27.0"
|
|
38
43
|
},
|
package/src/arithmetic.h
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#ifndef TREE_SITTER_SH_ARITHMETIC_H_
|
|
2
2
|
#define TREE_SITTER_SH_ARITHMETIC_H_
|
|
3
3
|
|
|
4
|
+
#include "source.h"
|
|
4
5
|
#include "tree_sitter/alloc.h"
|
|
5
6
|
|
|
6
7
|
#include <stdbool.h>
|
|
@@ -110,31 +111,6 @@ struct DynamicArithmeticGraph {
|
|
|
110
111
|
bool valid;
|
|
111
112
|
};
|
|
112
113
|
|
|
113
|
-
static void *dynamic_arithmetic_grow(
|
|
114
|
-
void *buffer,
|
|
115
|
-
size_t *capacity,
|
|
116
|
-
size_t length,
|
|
117
|
-
size_t width
|
|
118
|
-
) {
|
|
119
|
-
if (length <= *capacity) {
|
|
120
|
-
return buffer;
|
|
121
|
-
}
|
|
122
|
-
size_t maximum = SIZE_MAX / width;
|
|
123
|
-
if (length > maximum) {
|
|
124
|
-
return NULL;
|
|
125
|
-
}
|
|
126
|
-
size_t next = *capacity == 0 ? 16 : *capacity;
|
|
127
|
-
while (next < length) {
|
|
128
|
-
next = next > maximum / 2 ? maximum : next * 2;
|
|
129
|
-
}
|
|
130
|
-
void *grown = ts_realloc(buffer, next * width);
|
|
131
|
-
if (grown == NULL) {
|
|
132
|
-
return NULL;
|
|
133
|
-
}
|
|
134
|
-
*capacity = next;
|
|
135
|
-
return grown;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
114
|
static bool arithmetic_whitespace(int32_t character) {
|
|
139
115
|
return character ==
|
|
140
116
|
' ' ||
|
|
@@ -147,25 +123,29 @@ static bool arithmetic_whitespace(int32_t character) {
|
|
|
147
123
|
character == '\f';
|
|
148
124
|
}
|
|
149
125
|
|
|
150
|
-
static bool
|
|
126
|
+
static bool is_decimal_digit(int32_t character) {
|
|
127
|
+
return character >= '0' && character <= '9';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static bool is_hexadecimal_digit(int32_t character) {
|
|
131
|
+
return is_decimal_digit(character) ||
|
|
132
|
+
(character >= 'A' && character <= 'F') ||
|
|
133
|
+
(character >= 'a' && character <= 'f');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
static bool is_name_start_character(int32_t character) {
|
|
151
137
|
return (character >= 'A' && character <= 'Z') ||
|
|
152
138
|
(character >= 'a' && character <= 'z') ||
|
|
153
139
|
character == '_';
|
|
154
140
|
}
|
|
155
141
|
|
|
156
|
-
static bool
|
|
157
|
-
return character
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
static bool dynamic_arithmetic_hex(int32_t character) {
|
|
161
|
-
return dynamic_arithmetic_digit(character) ||
|
|
162
|
-
(character >= 'a' && character <= 'f') ||
|
|
163
|
-
(character >= 'A' && character <= 'F');
|
|
142
|
+
static bool is_name_character(int32_t character) {
|
|
143
|
+
return is_name_start_character(character) || is_decimal_digit(character);
|
|
164
144
|
}
|
|
165
145
|
|
|
166
146
|
static int dynamic_arithmetic_lex(uint8_t state, int32_t character) {
|
|
167
|
-
bool name =
|
|
168
|
-
bool digit =
|
|
147
|
+
bool name = is_name_start_character(character);
|
|
148
|
+
bool digit = is_decimal_digit(character);
|
|
169
149
|
switch (state) {
|
|
170
150
|
case DYNAMIC_LEX_START:
|
|
171
151
|
if (arithmetic_whitespace(character)) {
|
|
@@ -236,10 +216,10 @@ static int dynamic_arithmetic_lex(uint8_t state, int32_t character) {
|
|
|
236
216
|
}
|
|
237
217
|
return name ? DYNAMIC_LEX_INVALID : DYNAMIC_LEX_STOP;
|
|
238
218
|
case DYNAMIC_LEX_HEX_PREFIX:
|
|
239
|
-
return
|
|
240
|
-
|
|
219
|
+
return is_hexadecimal_digit(character) ? DYNAMIC_LEX_HEX
|
|
220
|
+
: DYNAMIC_LEX_INVALID;
|
|
241
221
|
case DYNAMIC_LEX_HEX:
|
|
242
|
-
if (
|
|
222
|
+
if (is_hexadecimal_digit(character)) {
|
|
243
223
|
return DYNAMIC_LEX_HEX;
|
|
244
224
|
}
|
|
245
225
|
return name || digit ? DYNAMIC_LEX_INVALID : DYNAMIC_LEX_STOP;
|
|
@@ -369,7 +349,7 @@ static bool dynamic_arithmetic_vertex(
|
|
|
369
349
|
if (graph->vertex_count == SIZE_MAX) {
|
|
370
350
|
return false;
|
|
371
351
|
}
|
|
372
|
-
struct DynamicArithmeticVertex *vertices =
|
|
352
|
+
struct DynamicArithmeticVertex *vertices = source_grow(
|
|
373
353
|
graph->vertices,
|
|
374
354
|
&graph->vertex_capacity,
|
|
375
355
|
graph->vertex_count + 1,
|
|
@@ -400,7 +380,7 @@ static bool dynamic_arithmetic_edge(
|
|
|
400
380
|
) {
|
|
401
381
|
return false;
|
|
402
382
|
}
|
|
403
|
-
struct DynamicArithmeticEdge *edges =
|
|
383
|
+
struct DynamicArithmeticEdge *edges = source_grow(
|
|
404
384
|
graph->edges,
|
|
405
385
|
&graph->edge_capacity,
|
|
406
386
|
graph->edge_count + 1,
|
|
@@ -434,7 +414,7 @@ static bool dynamic_arithmetic_enqueue(
|
|
|
434
414
|
if (graph->pending_count == SIZE_MAX) {
|
|
435
415
|
return false;
|
|
436
416
|
}
|
|
437
|
-
struct DynamicArithmeticLexItem *pending =
|
|
417
|
+
struct DynamicArithmeticLexItem *pending = source_grow(
|
|
438
418
|
graph->pending,
|
|
439
419
|
&graph->pending_capacity,
|
|
440
420
|
graph->pending_count + 1,
|
|
@@ -477,7 +457,7 @@ static bool dynamic_arithmetic_emit(
|
|
|
477
457
|
if (graph->endpoint_count == SIZE_MAX) {
|
|
478
458
|
return false;
|
|
479
459
|
}
|
|
480
|
-
size_t *endpoints =
|
|
460
|
+
size_t *endpoints = source_grow(
|
|
481
461
|
graph->endpoints,
|
|
482
462
|
&graph->endpoint_capacity,
|
|
483
463
|
graph->endpoint_count + 1,
|
|
@@ -669,7 +649,7 @@ static bool dynamic_arithmetic_project_enqueue(
|
|
|
669
649
|
if (projection->count == SIZE_MAX) {
|
|
670
650
|
return false;
|
|
671
651
|
}
|
|
672
|
-
size_t *pending =
|
|
652
|
+
size_t *pending = source_grow(
|
|
673
653
|
projection->pending,
|
|
674
654
|
&projection->capacity,
|
|
675
655
|
projection->count + 1,
|
|
@@ -954,7 +934,7 @@ static bool dynamic_arithmetic_add_item(
|
|
|
954
934
|
if (chart->item_count == SIZE_MAX) {
|
|
955
935
|
return false;
|
|
956
936
|
}
|
|
957
|
-
struct DynamicArithmeticItem *items =
|
|
937
|
+
struct DynamicArithmeticItem *items = source_grow(
|
|
958
938
|
chart->items,
|
|
959
939
|
&chart->item_capacity,
|
|
960
940
|
chart->item_count + 1,
|
|
@@ -964,7 +944,7 @@ static bool dynamic_arithmetic_add_item(
|
|
|
964
944
|
return false;
|
|
965
945
|
}
|
|
966
946
|
chart->items = items;
|
|
967
|
-
size_t *pending =
|
|
947
|
+
size_t *pending = source_grow(
|
|
968
948
|
chart->pending,
|
|
969
949
|
&chart->pending_capacity,
|
|
970
950
|
chart->pending_count + 1,
|
|
@@ -1059,7 +1039,7 @@ static bool dynamic_arithmetic_complete(
|
|
|
1059
1039
|
if (chart->completion_count == SIZE_MAX) {
|
|
1060
1040
|
return false;
|
|
1061
1041
|
}
|
|
1062
|
-
struct DynamicArithmeticCompletion *completions =
|
|
1042
|
+
struct DynamicArithmeticCompletion *completions = source_grow(
|
|
1063
1043
|
chart->completions,
|
|
1064
1044
|
&chart->completion_capacity,
|
|
1065
1045
|
chart->completion_count + 1,
|