@cplieger/deadset-ts 8.0.0-dev.12 → 8.0.0-dev.14
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/README.md +2 -0
- package/THIRD_PARTY_NOTICES.md +27 -0
- package/dist/src/build-scripts.js +6 -13
- package/dist/src/commands/read.js +156 -0
- package/dist/src/commands/shell.js +75 -0
- package/dist/src/commands/tools.js +460 -0
- package/dist/src/configuration-files.js +11 -25
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/dependencies.js +67 -115
- package/dist/src/load/synthesize.js +12 -5
- package/dist/src/manifest.js +27 -14
- package/dist/src/roots.js +18 -11
- package/dist/src/text/named-files.js +24 -3
- package/dist/src/text/yaml.js +464 -173
- package/dist/src/vendor/unbash/ansi-c.js +132 -0
- package/dist/src/vendor/unbash/arithmetic-parts.js +37 -0
- package/dist/src/vendor/unbash/arithmetic.js +591 -0
- package/dist/src/vendor/unbash/assignment.js +131 -0
- package/dist/src/vendor/unbash/chars.js +48 -0
- package/dist/src/vendor/unbash/command.js +41 -0
- package/dist/src/vendor/unbash/expansion.js +80 -0
- package/dist/src/vendor/unbash/glob.js +268 -0
- package/dist/src/vendor/unbash/heredoc.js +27 -0
- package/dist/src/vendor/unbash/internal-types.js +1 -0
- package/dist/src/vendor/unbash/lexer.js +4274 -0
- package/dist/src/vendor/unbash/limits.js +3 -0
- package/dist/src/vendor/unbash/parser.js +1366 -0
- package/dist/src/vendor/unbash/parts.js +57 -0
- package/dist/src/vendor/unbash/substitution-context.js +8 -0
- package/dist/src/vendor/unbash/traversal.js +212 -0
- package/dist/src/vendor/unbash/types.js +1 -0
- package/dist/src/vendor/unbash/word-value.js +61 -0
- package/dist/src/vendor/unbash/word.js +40 -0
- package/dist/src/workflow-steps.js +111 -133
- package/jsr.json +2 -1
- package/package.json +4 -2
- package/src/build-scripts.ts +6 -13
- package/src/commands/read.ts +230 -0
- package/src/commands/shell.ts +126 -0
- package/src/commands/tools.ts +618 -0
- package/src/configuration-files.ts +10 -26
- package/src/conformance-record.ts +1 -1
- package/src/dependencies.ts +99 -123
- package/src/load/synthesize.ts +18 -5
- package/src/manifest.ts +43 -25
- package/src/roots.ts +20 -13
- package/src/text/named-files.ts +26 -3
- package/src/text/yaml.ts +503 -179
- package/src/vendor/unbash/LICENSE +15 -0
- package/src/vendor/unbash/VENDORED.md +16 -0
- package/src/vendor/unbash/ansi-c.ts +132 -0
- package/src/vendor/unbash/arithmetic-parts.ts +53 -0
- package/src/vendor/unbash/arithmetic.ts +641 -0
- package/src/vendor/unbash/assignment.ts +163 -0
- package/src/vendor/unbash/chars.ts +48 -0
- package/src/vendor/unbash/command.ts +47 -0
- package/src/vendor/unbash/expansion.ts +144 -0
- package/src/vendor/unbash/glob.ts +284 -0
- package/src/vendor/unbash/heredoc.ts +34 -0
- package/src/vendor/unbash/internal-types.ts +94 -0
- package/src/vendor/unbash/lexer.ts +4352 -0
- package/src/vendor/unbash/limits.ts +3 -0
- package/src/vendor/unbash/parser.ts +1612 -0
- package/src/vendor/unbash/parts.ts +96 -0
- package/src/vendor/unbash/substitution-context.ts +20 -0
- package/src/vendor/unbash/traversal.ts +182 -0
- package/src/vendor/unbash/types.ts +660 -0
- package/src/vendor/unbash/word-value.ts +64 -0
- package/src/vendor/unbash/word.ts +50 -0
- package/src/workflow-steps.ts +153 -154
- package/dist/src/text/shell.js +0 -159
- package/src/text/named-files.inputs.json +0 -1522
- package/src/text/shell.ts +0 -161
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
// Code generated by scripts/vendor-unbash.ts from unbash 6.0.0 (968c14992a43dc06ce29c9f51858abd4a87851ea). DO NOT EDIT.
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import type { ArithmeticExpression, ArithmeticWord } from "./internal-types.ts";
|
|
4
|
+
import { ScriptExpansionImpl } from "./expansion.ts";
|
|
5
|
+
import { MAX_SYNTAX_NESTING } from "./limits.ts";
|
|
6
|
+
import {
|
|
7
|
+
CH_TAB,
|
|
8
|
+
CH_NL,
|
|
9
|
+
CH_SPACE,
|
|
10
|
+
CH_BANG,
|
|
11
|
+
CH_DOLLAR,
|
|
12
|
+
CH_PERCENT,
|
|
13
|
+
CH_AMP,
|
|
14
|
+
CH_LPAREN,
|
|
15
|
+
CH_RPAREN,
|
|
16
|
+
CH_STAR,
|
|
17
|
+
CH_PLUS,
|
|
18
|
+
CH_COMMA,
|
|
19
|
+
CH_DASH,
|
|
20
|
+
CH_SLASH,
|
|
21
|
+
CH_0,
|
|
22
|
+
CH_9,
|
|
23
|
+
CH_COLON,
|
|
24
|
+
CH_LT,
|
|
25
|
+
CH_EQ,
|
|
26
|
+
CH_GT,
|
|
27
|
+
CH_QUESTION,
|
|
28
|
+
CH_A,
|
|
29
|
+
CH_Z,
|
|
30
|
+
CH_LBRACKET,
|
|
31
|
+
CH_RBRACKET,
|
|
32
|
+
CH_CARET,
|
|
33
|
+
CH_UNDERSCORE,
|
|
34
|
+
CH_a,
|
|
35
|
+
CH_z,
|
|
36
|
+
CH_LBRACE,
|
|
37
|
+
CH_RBRACE,
|
|
38
|
+
CH_PIPE,
|
|
39
|
+
CH_TILDE,
|
|
40
|
+
} from "./chars.ts";
|
|
41
|
+
|
|
42
|
+
function opPrec(op: string): number {
|
|
43
|
+
switch (op) {
|
|
44
|
+
case ",":
|
|
45
|
+
return 1;
|
|
46
|
+
case "=":
|
|
47
|
+
case "+=":
|
|
48
|
+
case "-=":
|
|
49
|
+
case "*=":
|
|
50
|
+
case "/=":
|
|
51
|
+
case "%=":
|
|
52
|
+
case "<<=":
|
|
53
|
+
case ">>=":
|
|
54
|
+
case "&=":
|
|
55
|
+
case "|=":
|
|
56
|
+
case "^=":
|
|
57
|
+
return 2;
|
|
58
|
+
case "||":
|
|
59
|
+
return 4;
|
|
60
|
+
case "&&":
|
|
61
|
+
return 5;
|
|
62
|
+
case "|":
|
|
63
|
+
return 6;
|
|
64
|
+
case "^":
|
|
65
|
+
return 7;
|
|
66
|
+
case "&":
|
|
67
|
+
return 8;
|
|
68
|
+
case "==":
|
|
69
|
+
case "!=":
|
|
70
|
+
return 9;
|
|
71
|
+
case "<":
|
|
72
|
+
case "<=":
|
|
73
|
+
case ">":
|
|
74
|
+
case ">=":
|
|
75
|
+
return 10;
|
|
76
|
+
case "<<":
|
|
77
|
+
case ">>":
|
|
78
|
+
return 11;
|
|
79
|
+
case "+":
|
|
80
|
+
case "-":
|
|
81
|
+
return 12;
|
|
82
|
+
case "*":
|
|
83
|
+
case "/":
|
|
84
|
+
case "%":
|
|
85
|
+
return 13;
|
|
86
|
+
case "**":
|
|
87
|
+
return 14;
|
|
88
|
+
case "?":
|
|
89
|
+
return 3;
|
|
90
|
+
default:
|
|
91
|
+
return -1;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function trimEnd(src: string, start: number, end: number): number {
|
|
96
|
+
while (end > start) {
|
|
97
|
+
const c = src.charCodeAt(end - 1);
|
|
98
|
+
if (c !== CH_SPACE && c !== CH_TAB && c !== CH_NL) break;
|
|
99
|
+
end--;
|
|
100
|
+
}
|
|
101
|
+
return end;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function opRightAssoc(op: string): boolean {
|
|
105
|
+
switch (op) {
|
|
106
|
+
case "=":
|
|
107
|
+
case "+=":
|
|
108
|
+
case "-=":
|
|
109
|
+
case "*=":
|
|
110
|
+
case "/=":
|
|
111
|
+
case "%=":
|
|
112
|
+
case "<<=":
|
|
113
|
+
case ">>=":
|
|
114
|
+
case "&=":
|
|
115
|
+
case "|=":
|
|
116
|
+
case "^=":
|
|
117
|
+
case "**":
|
|
118
|
+
return true;
|
|
119
|
+
default:
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface ArithmeticParseCollector {
|
|
125
|
+
embeddedWords: ArithmeticWord[];
|
|
126
|
+
// Builds a `$(…)` node whose script defers against the enclosing source and nesting budget.
|
|
127
|
+
commandExpansion: (pos: number, end: number, text: string) => ScriptExpansionImpl<"ArithmeticCommandExpansion">;
|
|
128
|
+
findClosingBracket?: (start: number, end: number) => number;
|
|
129
|
+
// Embedded `$(`, `${`, and `$((` require the lexer's shell-aware delimiter scanners;
|
|
130
|
+
// bodies parsed without a collector cannot contain them (see hasEmbeddedWordStructure).
|
|
131
|
+
findClosingBrace: (start: number, end: number) => number;
|
|
132
|
+
findClosingParenthesis: (start: number, end: number) => number;
|
|
133
|
+
findArithmeticExpansionEnd: (start: number, end: number) => number;
|
|
134
|
+
findArithmeticWordEnd?: (start: number, end: number) => number;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function parseArithmeticExpression(
|
|
138
|
+
src: string,
|
|
139
|
+
offset: number = 0,
|
|
140
|
+
collector?: ArithmeticParseCollector,
|
|
141
|
+
nestingDepth = 0,
|
|
142
|
+
): ArithmeticExpression | null {
|
|
143
|
+
let pos = 0;
|
|
144
|
+
const len = src.length;
|
|
145
|
+
const initialWordCount = collector?.embeddedWords.length ?? 0;
|
|
146
|
+
|
|
147
|
+
function makeWord(start: number, end: number, embedded = false): ArithmeticWord {
|
|
148
|
+
const node: ArithmeticWord = {
|
|
149
|
+
type: "ArithmeticWord",
|
|
150
|
+
pos: start + offset,
|
|
151
|
+
end: end + offset,
|
|
152
|
+
value: src.slice(start, end),
|
|
153
|
+
parts: undefined,
|
|
154
|
+
};
|
|
155
|
+
if (embedded) collector?.embeddedWords.push(node);
|
|
156
|
+
return node;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function skipWS(): void {
|
|
160
|
+
while (pos < len) {
|
|
161
|
+
const c = src.charCodeAt(pos);
|
|
162
|
+
if (c === CH_SPACE || c === CH_TAB || c === CH_NL) pos++;
|
|
163
|
+
else break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function tryReadBinOp(): string | null {
|
|
168
|
+
if (pos >= len) return null;
|
|
169
|
+
const c = src.charCodeAt(pos);
|
|
170
|
+
const nc = pos + 1 < len ? src.charCodeAt(pos + 1) : 0;
|
|
171
|
+
const nnc = pos + 2 < len ? src.charCodeAt(pos + 2) : 0;
|
|
172
|
+
|
|
173
|
+
switch (c) {
|
|
174
|
+
case CH_COMMA:
|
|
175
|
+
pos++;
|
|
176
|
+
return ",";
|
|
177
|
+
case CH_EQ:
|
|
178
|
+
if (nc === CH_EQ) {
|
|
179
|
+
pos += 2;
|
|
180
|
+
return "==";
|
|
181
|
+
}
|
|
182
|
+
pos++;
|
|
183
|
+
return "=";
|
|
184
|
+
case CH_BANG:
|
|
185
|
+
if (nc === CH_EQ) {
|
|
186
|
+
pos += 2;
|
|
187
|
+
return "!=";
|
|
188
|
+
}
|
|
189
|
+
return null; // unary
|
|
190
|
+
case CH_LT:
|
|
191
|
+
if (nc === CH_LT) {
|
|
192
|
+
if (nnc === CH_EQ) {
|
|
193
|
+
pos += 3;
|
|
194
|
+
return "<<=";
|
|
195
|
+
}
|
|
196
|
+
pos += 2;
|
|
197
|
+
return "<<";
|
|
198
|
+
}
|
|
199
|
+
if (nc === CH_EQ) {
|
|
200
|
+
pos += 2;
|
|
201
|
+
return "<=";
|
|
202
|
+
}
|
|
203
|
+
pos++;
|
|
204
|
+
return "<";
|
|
205
|
+
case CH_GT:
|
|
206
|
+
if (nc === CH_GT) {
|
|
207
|
+
if (nnc === CH_EQ) {
|
|
208
|
+
pos += 3;
|
|
209
|
+
return ">>=";
|
|
210
|
+
}
|
|
211
|
+
pos += 2;
|
|
212
|
+
return ">>";
|
|
213
|
+
}
|
|
214
|
+
if (nc === CH_EQ) {
|
|
215
|
+
pos += 2;
|
|
216
|
+
return ">=";
|
|
217
|
+
}
|
|
218
|
+
pos++;
|
|
219
|
+
return ">";
|
|
220
|
+
case CH_PLUS:
|
|
221
|
+
if (nc === CH_EQ) {
|
|
222
|
+
pos += 2;
|
|
223
|
+
return "+=";
|
|
224
|
+
}
|
|
225
|
+
if (nc === CH_PLUS) return null; // postfix/prefix, not binary
|
|
226
|
+
pos++;
|
|
227
|
+
return "+";
|
|
228
|
+
case CH_DASH:
|
|
229
|
+
if (nc === CH_EQ) {
|
|
230
|
+
pos += 2;
|
|
231
|
+
return "-=";
|
|
232
|
+
}
|
|
233
|
+
if (nc === CH_DASH) return null; // postfix/prefix, not binary
|
|
234
|
+
pos++;
|
|
235
|
+
return "-";
|
|
236
|
+
case CH_STAR:
|
|
237
|
+
if (nc === CH_STAR) {
|
|
238
|
+
pos += 2;
|
|
239
|
+
return "**";
|
|
240
|
+
}
|
|
241
|
+
if (nc === CH_EQ) {
|
|
242
|
+
pos += 2;
|
|
243
|
+
return "*=";
|
|
244
|
+
}
|
|
245
|
+
pos++;
|
|
246
|
+
return "*";
|
|
247
|
+
case CH_SLASH:
|
|
248
|
+
if (nc === CH_EQ) {
|
|
249
|
+
pos += 2;
|
|
250
|
+
return "/=";
|
|
251
|
+
}
|
|
252
|
+
pos++;
|
|
253
|
+
return "/";
|
|
254
|
+
case CH_PERCENT:
|
|
255
|
+
if (nc === CH_EQ) {
|
|
256
|
+
pos += 2;
|
|
257
|
+
return "%=";
|
|
258
|
+
}
|
|
259
|
+
pos++;
|
|
260
|
+
return "%";
|
|
261
|
+
case CH_PIPE:
|
|
262
|
+
if (nc === CH_PIPE) {
|
|
263
|
+
pos += 2;
|
|
264
|
+
return "||";
|
|
265
|
+
}
|
|
266
|
+
if (nc === CH_EQ) {
|
|
267
|
+
pos += 2;
|
|
268
|
+
return "|=";
|
|
269
|
+
}
|
|
270
|
+
pos++;
|
|
271
|
+
return "|";
|
|
272
|
+
case CH_AMP:
|
|
273
|
+
if (nc === CH_AMP) {
|
|
274
|
+
pos += 2;
|
|
275
|
+
return "&&";
|
|
276
|
+
}
|
|
277
|
+
if (nc === CH_EQ) {
|
|
278
|
+
pos += 2;
|
|
279
|
+
return "&=";
|
|
280
|
+
}
|
|
281
|
+
pos++;
|
|
282
|
+
return "&";
|
|
283
|
+
case CH_CARET:
|
|
284
|
+
if (nc === CH_EQ) {
|
|
285
|
+
pos += 2;
|
|
286
|
+
return "^=";
|
|
287
|
+
}
|
|
288
|
+
pos++;
|
|
289
|
+
return "^";
|
|
290
|
+
case CH_QUESTION:
|
|
291
|
+
pos++;
|
|
292
|
+
return "?";
|
|
293
|
+
default:
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function skipTruncatedUnary(): void {
|
|
299
|
+
skipWS();
|
|
300
|
+
while (pos < len) {
|
|
301
|
+
const c = src.charCodeAt(pos);
|
|
302
|
+
if (c !== CH_BANG && c !== CH_TILDE && c !== CH_PLUS && c !== CH_DASH) break;
|
|
303
|
+
if (src.charCodeAt(pos + 1) === CH_EQ) break;
|
|
304
|
+
pos++;
|
|
305
|
+
skipWS();
|
|
306
|
+
}
|
|
307
|
+
if (src.charCodeAt(pos) === CH_LPAREN) {
|
|
308
|
+
let groups = 1;
|
|
309
|
+
pos++;
|
|
310
|
+
while (pos < len && groups > 0) {
|
|
311
|
+
const wordEnd = collector?.findArithmeticWordEnd?.(pos + offset, offset + len) ?? pos + offset;
|
|
312
|
+
if (wordEnd > pos + offset) {
|
|
313
|
+
pos = wordEnd - offset;
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
const c = src.charCodeAt(pos++);
|
|
317
|
+
if (c === CH_LPAREN) groups++;
|
|
318
|
+
else if (c === CH_RPAREN) groups--;
|
|
319
|
+
}
|
|
320
|
+
} else if (pos < len && src.charCodeAt(pos) !== CH_RPAREN && src.charCodeAt(pos) !== CH_COLON) {
|
|
321
|
+
parseAtom(0);
|
|
322
|
+
}
|
|
323
|
+
skipWS();
|
|
324
|
+
const c = src.charCodeAt(pos);
|
|
325
|
+
if ((c === CH_PLUS || c === CH_DASH) && src.charCodeAt(pos + 1) === c) pos += 2;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function recoverExpression(minPrec: number): ArithmeticWord {
|
|
329
|
+
skipWS();
|
|
330
|
+
const start = pos;
|
|
331
|
+
if (start === len) return makeWord(start, start);
|
|
332
|
+
const wordCount = collector?.embeddedWords.length ?? 0;
|
|
333
|
+
let ternaries = 0;
|
|
334
|
+
for (;;) {
|
|
335
|
+
skipTruncatedUnary();
|
|
336
|
+
skipWS();
|
|
337
|
+
if (src.charCodeAt(pos) === CH_COLON && ternaries > 0) {
|
|
338
|
+
ternaries--;
|
|
339
|
+
pos++;
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
const saved = pos;
|
|
343
|
+
const op = tryReadBinOp();
|
|
344
|
+
if (op === null || (ternaries === 0 && opPrec(op) < minPrec)) {
|
|
345
|
+
pos = saved;
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
if (op === "?") ternaries++;
|
|
349
|
+
}
|
|
350
|
+
if (collector) collector.embeddedWords.length = wordCount;
|
|
351
|
+
return makeWord(start, trimEnd(src, start, pos));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function parseBinExpr(minPrec: number, depth: number): ArithmeticExpression {
|
|
355
|
+
if (depth > MAX_SYNTAX_NESTING) return recoverExpression(minPrec);
|
|
356
|
+
let left = parseUnaryExpr(depth);
|
|
357
|
+
|
|
358
|
+
while (true) {
|
|
359
|
+
skipWS();
|
|
360
|
+
if (pos >= len) break;
|
|
361
|
+
|
|
362
|
+
const saved = pos;
|
|
363
|
+
const op = tryReadBinOp();
|
|
364
|
+
if (!op) break;
|
|
365
|
+
|
|
366
|
+
// Ternary
|
|
367
|
+
if (op === "?") {
|
|
368
|
+
if (opPrec(op) < minPrec) {
|
|
369
|
+
pos = saved;
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
const consequent = parseBinExpr(1, depth + 1);
|
|
373
|
+
skipWS();
|
|
374
|
+
if (pos < len && src.charCodeAt(pos) === CH_COLON) pos++;
|
|
375
|
+
const alternate = parseBinExpr(3, depth + 1);
|
|
376
|
+
left = { type: "ArithmeticTernary", pos: left.pos, end: alternate.end, test: left, consequent, alternate };
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const prec = opPrec(op);
|
|
381
|
+
if (prec < minPrec) {
|
|
382
|
+
pos = saved;
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const nextPrec = opRightAssoc(op) ? prec : prec + 1;
|
|
387
|
+
const right = parseBinExpr(nextPrec, depth + 1);
|
|
388
|
+
left = { type: "ArithmeticBinary", pos: left.pos, end: right.end, operator: op, left, right };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return left;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function parseUnaryExpr(depth: number): ArithmeticExpression {
|
|
395
|
+
skipWS();
|
|
396
|
+
if (depth > MAX_SYNTAX_NESTING) return recoverExpression(15);
|
|
397
|
+
if (pos >= len) return makeWord(pos, pos);
|
|
398
|
+
|
|
399
|
+
const start = pos;
|
|
400
|
+
const c = src.charCodeAt(pos);
|
|
401
|
+
const nc = pos + 1 < len ? src.charCodeAt(pos + 1) : 0;
|
|
402
|
+
|
|
403
|
+
// Prefix ++ --
|
|
404
|
+
if (c === CH_PLUS && nc === CH_PLUS) {
|
|
405
|
+
pos += 2;
|
|
406
|
+
const operand = parseUnaryExpr(depth + 1);
|
|
407
|
+
return { type: "ArithmeticUnary", pos: start + offset, end: operand.end, operator: "++", operand, prefix: true };
|
|
408
|
+
}
|
|
409
|
+
if (c === CH_DASH && nc === CH_DASH) {
|
|
410
|
+
pos += 2;
|
|
411
|
+
const operand = parseUnaryExpr(depth + 1);
|
|
412
|
+
return { type: "ArithmeticUnary", pos: start + offset, end: operand.end, operator: "--", operand, prefix: true };
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Unary ! ~ + -
|
|
416
|
+
if (c === CH_BANG) {
|
|
417
|
+
pos++;
|
|
418
|
+
const operand = parseUnaryExpr(depth + 1);
|
|
419
|
+
return { type: "ArithmeticUnary", pos: start + offset, end: operand.end, operator: "!", operand, prefix: true };
|
|
420
|
+
}
|
|
421
|
+
if (c === CH_TILDE) {
|
|
422
|
+
pos++;
|
|
423
|
+
const operand = parseUnaryExpr(depth + 1);
|
|
424
|
+
return { type: "ArithmeticUnary", pos: start + offset, end: operand.end, operator: "~", operand, prefix: true };
|
|
425
|
+
}
|
|
426
|
+
if (c === CH_PLUS && nc !== CH_PLUS && nc !== CH_EQ) {
|
|
427
|
+
pos++;
|
|
428
|
+
const operand = parseUnaryExpr(depth + 1);
|
|
429
|
+
return { type: "ArithmeticUnary", pos: start + offset, end: operand.end, operator: "+", operand, prefix: true };
|
|
430
|
+
}
|
|
431
|
+
if (c === CH_DASH && nc !== CH_DASH && nc !== CH_EQ) {
|
|
432
|
+
pos++;
|
|
433
|
+
const operand = parseUnaryExpr(depth + 1);
|
|
434
|
+
return { type: "ArithmeticUnary", pos: start + offset, end: operand.end, operator: "-", operand, prefix: true };
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return parsePostfixExpr(depth);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function parsePostfixExpr(depth: number): ArithmeticExpression {
|
|
441
|
+
const operand = parseAtom(depth);
|
|
442
|
+
skipWS();
|
|
443
|
+
if (pos + 1 < len) {
|
|
444
|
+
const c = src.charCodeAt(pos);
|
|
445
|
+
const nc = src.charCodeAt(pos + 1);
|
|
446
|
+
if (c === CH_PLUS && nc === CH_PLUS) {
|
|
447
|
+
pos += 2;
|
|
448
|
+
return { type: "ArithmeticUnary", pos: operand.pos, end: pos + offset, operator: "++", operand, prefix: false };
|
|
449
|
+
}
|
|
450
|
+
if (c === CH_DASH && nc === CH_DASH) {
|
|
451
|
+
pos += 2;
|
|
452
|
+
return { type: "ArithmeticUnary", pos: operand.pos, end: pos + offset, operator: "--", operand, prefix: false };
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return operand;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function parseAtom(depth: number): ArithmeticExpression {
|
|
459
|
+
skipWS();
|
|
460
|
+
if (pos >= len) return makeWord(pos, pos);
|
|
461
|
+
|
|
462
|
+
const c = src.charCodeAt(pos);
|
|
463
|
+
|
|
464
|
+
// Parenthesized expression
|
|
465
|
+
if (c === CH_LPAREN) {
|
|
466
|
+
const start = pos;
|
|
467
|
+
pos++;
|
|
468
|
+
const expr = parseBinExpr(0, depth + 1);
|
|
469
|
+
skipWS();
|
|
470
|
+
if (pos < len && src.charCodeAt(pos) === CH_RPAREN) pos++;
|
|
471
|
+
return { type: "ArithmeticGroup", pos: start + offset, end: pos + offset, expression: expr };
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Dollar expansion
|
|
475
|
+
if (c === CH_DOLLAR) {
|
|
476
|
+
const start = pos;
|
|
477
|
+
const wordCount = collector?.embeddedWords.length ?? 0;
|
|
478
|
+
const atom = readDollarAtom();
|
|
479
|
+
const wordEnd = collector?.findArithmeticWordEnd?.(start + offset, offset + len) ?? pos + offset;
|
|
480
|
+
if (wordEnd > pos + offset) {
|
|
481
|
+
if (collector) collector.embeddedWords.length = wordCount;
|
|
482
|
+
pos = wordEnd - offset;
|
|
483
|
+
return makeWord(start, pos, true);
|
|
484
|
+
}
|
|
485
|
+
return atom;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (c === 0x60 /* ` */ || c === 0x22 /* " */ || c === 0x27 /* ' */) {
|
|
489
|
+
const start = pos;
|
|
490
|
+
pos = (collector?.findArithmeticWordEnd?.(start + offset, offset + len) ?? start + offset + 1) - offset;
|
|
491
|
+
return makeWord(start, pos, true);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Number or variable name
|
|
495
|
+
const start = pos;
|
|
496
|
+
const wordCount = collector?.embeddedWords.length ?? 0;
|
|
497
|
+
const atom = readWordAtom();
|
|
498
|
+
const wordEnd = collector?.findArithmeticWordEnd?.(start + offset, offset + len) ?? pos + offset;
|
|
499
|
+
if (wordEnd > pos + offset) {
|
|
500
|
+
if (collector) collector.embeddedWords.length = wordCount;
|
|
501
|
+
pos = wordEnd - offset;
|
|
502
|
+
return makeWord(start, pos, true);
|
|
503
|
+
}
|
|
504
|
+
return atom;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function readDollarAtom(): ArithmeticExpression {
|
|
508
|
+
const start = pos;
|
|
509
|
+
pos++; // skip $
|
|
510
|
+
if (pos >= len) return makeWord(start, pos);
|
|
511
|
+
|
|
512
|
+
const c = src.charCodeAt(pos);
|
|
513
|
+
|
|
514
|
+
if (c === CH_LPAREN) {
|
|
515
|
+
if (pos + 1 < len && src.charCodeAt(pos + 1) === CH_LPAREN) {
|
|
516
|
+
// $(( nested arithmetic ))
|
|
517
|
+
const expansionEnd = collector?.findArithmeticExpansionEnd(start + offset, offset + len) ?? -1;
|
|
518
|
+
if (expansionEnd !== -1) {
|
|
519
|
+
pos = expansionEnd - offset;
|
|
520
|
+
} else {
|
|
521
|
+
pos += 2;
|
|
522
|
+
let depth = 1;
|
|
523
|
+
while (pos < len && depth > 0) {
|
|
524
|
+
if (src.charCodeAt(pos) === CH_LPAREN && src.charCodeAt(pos + 1) === CH_LPAREN) {
|
|
525
|
+
depth++;
|
|
526
|
+
pos += 2;
|
|
527
|
+
} else if (src.charCodeAt(pos) === CH_RPAREN && src.charCodeAt(pos + 1) === CH_RPAREN) {
|
|
528
|
+
depth--;
|
|
529
|
+
pos += 2;
|
|
530
|
+
} else {
|
|
531
|
+
pos++;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
} else {
|
|
536
|
+
// $( command substitution )
|
|
537
|
+
pos++; // skip (
|
|
538
|
+
const close = collector?.findClosingParenthesis(pos + offset, offset + len) ?? -1;
|
|
539
|
+
if (close !== -1) {
|
|
540
|
+
pos = close - offset + 1;
|
|
541
|
+
} else {
|
|
542
|
+
let depth = 1;
|
|
543
|
+
while (pos < len && depth > 0) {
|
|
544
|
+
const ch = src.charCodeAt(pos++);
|
|
545
|
+
if (ch === CH_LPAREN) depth++;
|
|
546
|
+
else if (ch === CH_RPAREN) depth--;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
const text = src.slice(start, pos);
|
|
550
|
+
return collector
|
|
551
|
+
? collector.commandExpansion(start + offset, pos + offset, text)
|
|
552
|
+
: new ScriptExpansionImpl("ArithmeticCommandExpansion", start + offset, pos + offset, text);
|
|
553
|
+
}
|
|
554
|
+
} else if (c === CH_LBRACE) {
|
|
555
|
+
// ${ parameter expansion }
|
|
556
|
+
const close = collector?.findClosingBrace(pos + offset + 1, offset + len) ?? -1;
|
|
557
|
+
if (close !== -1) {
|
|
558
|
+
pos = close - offset + 1;
|
|
559
|
+
} else {
|
|
560
|
+
pos++;
|
|
561
|
+
let depth = 1;
|
|
562
|
+
while (pos < len && depth > 0) {
|
|
563
|
+
const ch = src.charCodeAt(pos++);
|
|
564
|
+
if (ch === CH_LBRACE) depth++;
|
|
565
|
+
else if (ch === CH_RBRACE) depth--;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
} else {
|
|
569
|
+
// $var
|
|
570
|
+
while (pos < len) {
|
|
571
|
+
const ch = src.charCodeAt(pos);
|
|
572
|
+
if (
|
|
573
|
+
(ch >= CH_a && ch <= CH_z) ||
|
|
574
|
+
(ch >= CH_A && ch <= CH_Z) ||
|
|
575
|
+
(ch >= CH_0 && ch <= CH_9) ||
|
|
576
|
+
ch === CH_UNDERSCORE
|
|
577
|
+
)
|
|
578
|
+
pos++;
|
|
579
|
+
else break;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
return makeWord(start, pos, c === CH_LPAREN || c === CH_LBRACE);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function readWordAtom(): ArithmeticExpression {
|
|
587
|
+
const start = pos;
|
|
588
|
+
|
|
589
|
+
while (pos < len) {
|
|
590
|
+
const c = src.charCodeAt(pos);
|
|
591
|
+
if (
|
|
592
|
+
(c >= CH_0 && c <= CH_9) ||
|
|
593
|
+
(c >= CH_A && c <= CH_Z) ||
|
|
594
|
+
(c >= CH_a && c <= CH_z) ||
|
|
595
|
+
c === CH_UNDERSCORE ||
|
|
596
|
+
c === 35 // # for base-N literals like 2#101
|
|
597
|
+
) {
|
|
598
|
+
pos++;
|
|
599
|
+
} else break;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Array subscript: var[expr]
|
|
603
|
+
if (pos > start && pos < len && src.charCodeAt(pos) === CH_LBRACKET) {
|
|
604
|
+
const close = collector?.findClosingBracket?.(pos + offset + 1, offset + len) ?? -1;
|
|
605
|
+
if (close !== -1) {
|
|
606
|
+
pos = close - offset + 1;
|
|
607
|
+
} else {
|
|
608
|
+
pos++;
|
|
609
|
+
let depth = 1;
|
|
610
|
+
while (pos < len && depth > 0) {
|
|
611
|
+
const c = src.charCodeAt(pos);
|
|
612
|
+
if (c === CH_LBRACKET) depth++;
|
|
613
|
+
else if (c === CH_RBRACKET) depth--;
|
|
614
|
+
pos++;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return makeWord(start, pos, true);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (pos === start) {
|
|
621
|
+
// Unknown character — advance to prevent infinite loop
|
|
622
|
+
pos++;
|
|
623
|
+
return makeWord(start, pos);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
return makeWord(start, pos);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
skipWS();
|
|
630
|
+
if (pos >= len) return null;
|
|
631
|
+
const start = pos;
|
|
632
|
+
const result = parseBinExpr(0, nestingDepth);
|
|
633
|
+
skipWS();
|
|
634
|
+
if (pos < len) {
|
|
635
|
+
// Unparsed tokens remain: a partial tree would silently drop them, so keep the whole body
|
|
636
|
+
// as one word. With embedded structure its parts still expose nested substitutions.
|
|
637
|
+
if (collector) collector.embeddedWords.length = initialWordCount;
|
|
638
|
+
return makeWord(start, trimEnd(src, start, len), collector !== undefined);
|
|
639
|
+
}
|
|
640
|
+
return result;
|
|
641
|
+
}
|