Skip to content

Commit 6836a71

Browse files
committed
fix(shell): share nested arithmetic context ancestry
1 parent 087a1d2 commit 6836a71

1 file changed

Lines changed: 21 additions & 14 deletions

File tree

  • apps/sim/lib/execution/code-placeholders

‎apps/sim/lib/execution/code-placeholders/shell.ts‎

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,17 @@ interface ShellScanFrame {
3232
bracketDepth?: number
3333
/** A bare name[index] is arithmetic only when its closing bracket is followed by assignment. */
3434
arrayAssignment?: boolean
35+
arithmeticParent?: ShellScanFrame
3536
literalRoot: boolean
3637
}
3738

3839
interface ShellOccurrenceContext {
3940
quote: ShellQuote
4041
/**
41-
* Enclosing arithmetic also re-evaluates nested substitutions. Keep frame references so a
42+
* Enclosing arithmetic also re-evaluates nested substitutions. Keep a frame reference so a
4243
* bare subscript can be classified when its closing bracket confirms an assignment.
4344
*/
44-
arithmeticFrames?: ShellScanFrame[]
45+
arithmeticFrame?: ShellScanFrame
4546
unsupported?: 'escaped sequence'
4647
}
4748

@@ -78,6 +79,12 @@ function shellArithmeticCommandStarts(code: string, index: number): boolean {
7879
return previous === undefined || /\s|[;&|()<>]/.test(previous)
7980
}
8081

82+
function pushShellFrame(frames: ShellScanFrame[], frame: ShellScanFrame): void {
83+
const parent = frames.at(-1)
84+
frame.arithmeticParent = parent?.kind === 'arithmetic' ? parent : parent?.arithmeticParent
85+
frames.push(frame)
86+
}
87+
8188
function readShellExpansionStart(
8289
code: string,
8390
index: number,
@@ -304,10 +311,10 @@ function parseHeredocHeaders(
304311
if (character === '\\') index += 1
305312
else if (character === '"') frame.quote = 'none'
306313
else if (expansion) {
307-
frames.push(expansion.frame)
314+
pushShellFrame(frames, expansion.frame)
308315
index += expansion.length - 1
309316
} else if (character === '`') {
310-
frames.push({
317+
pushShellFrame(frames, {
311318
kind: 'backtick',
312319
quote: 'none',
313320
parenthesisDepth: 0,
@@ -339,12 +346,12 @@ function parseHeredocHeaders(
339346
continue
340347
}
341348
if (expansion) {
342-
frames.push(expansion.frame)
349+
pushShellFrame(frames, expansion.frame)
343350
index += expansion.length - 1
344351
continue
345352
}
346353
if (character === '`') {
347-
frames.push({
354+
pushShellFrame(frames, {
348355
kind: 'backtick',
349356
quote: 'none',
350357
parenthesisDepth: 0,
@@ -483,8 +490,8 @@ function getUnsupportedShellPosition(
483490
occurrence: CodePlaceholderOccurrence,
484491
context: ShellOccurrenceContext
485492
): string | undefined {
486-
if (context.arithmeticFrames?.some((frame) => frame.arrayAssignment !== false)) {
487-
return 'in a shell arithmetic expression'
493+
for (let frame = context.arithmeticFrame; frame; frame = frame.arithmeticParent) {
494+
if (frame.arrayAssignment !== false) return 'in a shell arithmetic expression'
488495
}
489496
if (code[occurrence.start - 1] === '$') return 'immediately after "$"'
490497
if (context.quote !== 'none') return undefined
@@ -533,7 +540,7 @@ function collectShellOccurrenceContexts(
533540
if (occurrence) {
534541
contexts.set(occurrence, {
535542
quote: frame.quote,
536-
arithmeticFrames: frames.filter((candidate) => candidate.kind === 'arithmetic'),
543+
arithmeticFrame: frame.kind === 'arithmetic' ? frame : frame.arithmeticParent,
537544
})
538545
index = occurrence.end
539546
continue
@@ -567,7 +574,7 @@ function collectShellOccurrenceContexts(
567574
)
568575
const array = readShellArrayStart(code, index, frame)
569576
if (array) {
570-
frames.push(array)
577+
pushShellFrame(frames, array)
571578
index += 1
572579
continue
573580
}
@@ -584,10 +591,10 @@ function collectShellOccurrenceContexts(
584591
frame.quote = 'none'
585592
index += 1
586593
} else if (expansion) {
587-
frames.push(expansion.frame)
594+
pushShellFrame(frames, expansion.frame)
588595
index += expansion.length
589596
} else if (character === '`') {
590-
frames.push({
597+
pushShellFrame(frames, {
591598
kind: 'backtick',
592599
quote: 'none',
593600
parenthesisDepth: 0,
@@ -636,12 +643,12 @@ function collectShellOccurrenceContexts(
636643
continue
637644
}
638645
if (expansion) {
639-
frames.push(expansion.frame)
646+
pushShellFrame(frames, expansion.frame)
640647
index += expansion.length
641648
continue
642649
}
643650
if (character === '`') {
644-
frames.push({
651+
pushShellFrame(frames, {
645652
kind: 'backtick',
646653
quote: 'none',
647654
parenthesisDepth: 0,

0 commit comments

Comments
 (0)