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 CHANGED
@@ -1,30 +1,18 @@
1
1
  [package]
2
2
  name = "tree-sitter-sh"
3
- version = "0.15.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
- include = [
13
- "/LICENSE",
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.6"
23
+ cc = "1.4"
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # tree-sitter-sh
2
2
 
3
- [![CI](https://github.com/konomanoasa/tree-sitter-sh/actions/workflows/ci.yml/badge.svg)](https://github.com/konomanoasa/tree-sitter-sh/actions/workflows/ci.yml)
3
+ [![CI](https://github.com/konomanoasa/tree-sitter-sh/actions/workflows/ci.yaml/badge.svg)](https://github.com/konomanoasa/tree-sitter-sh/actions/workflows/ci.yaml)
4
4
  [![npm](https://img.shields.io/npm/v/tree-sitter-sh)](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 parse -- script.sh
27
+ npm run build
28
+ npm test
20
29
  ```
21
30
 
22
31
  ## Specification
@@ -1,9 +1,23 @@
1
+ use std::{env, path::Path};
2
+
1
3
  fn main() {
2
- println!("cargo::rerun-if-changed=src");
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
- cc::Build::new()
5
- .std("c17")
6
- .include("src")
7
- .files(["src/parser.c", "src/scanner.c"])
8
- .compile("tree-sitter-sh");
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
  }
@@ -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
- fn tree_sitter_sh() -> *const ();
4
+ fn tree_sitter_sh() -> *const ();
14
5
  }
15
6
 
16
- /// Grammar entry point for `tree_sitter::Parser::set_language`.
17
- pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_sh) };
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
- const keywordSource = ($, name) =>
79
+ // Alias the token directly so inserted MISSING tokens stay visible.
80
+ const wholeSource = ($, begin, lexeme, visible) =>
80
81
  choice(
81
- wholeLexical($, $[`_${name}_keyword_begin`], (source) =>
82
- alias(source, $[`${name}_keyword`]),
83
- ),
84
- alias($[`_${name}_keyword_lexeme`], $[`${name}_keyword`]),
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
- // Each delimiter reduces to one hidden node instead of two stack entries, so
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 = $._command_or_arithmetic_substitution_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
- // Only the consequence owns closing layout; a competing owner before fi
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
- _if_keyword_lexeme: ($) => lexical($, $._if_keyword_begin),
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
- seq(
1723
+ hereDocumentEndSource(
1724
+ $,
1728
1725
  $._quoted_here_document_end_begin,
1729
- repeat(
1730
- choice(
1731
- $._here_document_end_leading_tabs,
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
- seq(
1731
+ hereDocumentEndSource(
1732
+ $,
1740
1733
  $._here_document_end_begin,
1741
- repeat(
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
- _assignment_non_delimiter_part: ($) =>
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($._word_non_slash_part),
1842
- _assignment_tilde_user: ($) => repeat1($._assignment_non_delimiter_part),
1843
- _parameter_tilde_user: ($) => repeat1($._parameter_non_slash_part),
1844
- _word_non_slash_part: ($) =>
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
- alias($._word_initial_literal, $.literal),
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
- choice(
1875
- lexical($, $._word_initial_literal_begin),
1876
- wholeLexical($, $._word_initial_literal_begin),
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
- _parameter_non_slash_part: ($) =>
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($, $._command_or_arithmetic_substitution_start),
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.15.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.11",
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 dynamic_arithmetic_name_start(int32_t character) {
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 dynamic_arithmetic_digit(int32_t character) {
157
- return character >= '0' && character <= '9';
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 = dynamic_arithmetic_name_start(character);
168
- bool digit = dynamic_arithmetic_digit(character);
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 dynamic_arithmetic_hex(character) ? DYNAMIC_LEX_HEX
240
- : DYNAMIC_LEX_INVALID;
219
+ return is_hexadecimal_digit(character) ? DYNAMIC_LEX_HEX
220
+ : DYNAMIC_LEX_INVALID;
241
221
  case DYNAMIC_LEX_HEX:
242
- if (dynamic_arithmetic_hex(character)) {
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 = dynamic_arithmetic_grow(
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 = dynamic_arithmetic_grow(
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 = dynamic_arithmetic_grow(
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 = dynamic_arithmetic_grow(
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 = dynamic_arithmetic_grow(
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 = dynamic_arithmetic_grow(
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 = dynamic_arithmetic_grow(
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 = dynamic_arithmetic_grow(
1042
+ struct DynamicArithmeticCompletion *completions = source_grow(
1063
1043
  chart->completions,
1064
1044
  &chart->completion_capacity,
1065
1045
  chart->completion_count + 1,