Skip to content

Commit 43f8372

Browse files
authored
Merge branch 'main' into refactor/issue-881-errexit-suppression-helper
2 parents e2d8cce + 2928e36 commit 43f8372

File tree

5 files changed

+110
-10
lines changed

5 files changed

+110
-10
lines changed

crates/bashkit/src/interpreter/mod.rs

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4392,11 +4392,9 @@ impl Interpreter {
43924392
if is_internal_variable(var_name) {
43934393
continue;
43944394
}
4395-
// Handle compound array assignment: local -a arr=(1 2 3)
4396-
if (flags.array || flags.assoc)
4397-
&& value.starts_with('(')
4398-
&& value.ends_with(')')
4399-
{
4395+
// Handle compound array assignment: local arr=(1 2 3) or local -a/-A arr=(...)
4396+
let is_compound = value.starts_with('(') && value.ends_with(')');
4397+
if is_compound {
44004398
let inner = &value[1..value.len() - 1];
44014399
if flags.assoc {
44024400
let arr = self.assoc_arrays.entry(var_name.to_string()).or_default();
@@ -4505,7 +4503,54 @@ impl Interpreter {
45054503
if is_internal_variable(var_name) {
45064504
continue;
45074505
}
4508-
if flags.nameref {
4506+
let is_compound = value.starts_with('(') && value.ends_with(')');
4507+
if is_compound {
4508+
let inner = &value[1..value.len() - 1];
4509+
if flags.assoc {
4510+
let arr = self.assoc_arrays.entry(var_name.to_string()).or_default();
4511+
arr.clear();
4512+
let mut rest = inner.trim();
4513+
while let Some(bracket_start) = rest.find('[') {
4514+
if let Some(bracket_end) = rest[bracket_start..].find(']') {
4515+
let key = &rest[bracket_start + 1..bracket_start + bracket_end];
4516+
let after = &rest[bracket_start + bracket_end + 1..];
4517+
if let Some(eq_rest) = after.strip_prefix('=') {
4518+
let eq_rest = eq_rest.trim_start();
4519+
let (val, remainder) =
4520+
if let Some(stripped) = eq_rest.strip_prefix('"') {
4521+
if let Some(end_q) = stripped.find('"') {
4522+
(
4523+
&stripped[..end_q],
4524+
stripped[end_q + 1..].trim_start(),
4525+
)
4526+
} else {
4527+
(stripped.trim_end_matches('"'), "")
4528+
}
4529+
} else {
4530+
match eq_rest.find(char::is_whitespace) {
4531+
Some(sp) => {
4532+
(&eq_rest[..sp], eq_rest[sp..].trim_start())
4533+
}
4534+
None => (eq_rest, ""),
4535+
}
4536+
};
4537+
arr.insert(key.to_string(), val.to_string());
4538+
rest = remainder;
4539+
} else {
4540+
break;
4541+
}
4542+
} else {
4543+
break;
4544+
}
4545+
}
4546+
} else {
4547+
let arr = self.arrays.entry(var_name.to_string()).or_default();
4548+
arr.clear();
4549+
for (idx, val) in inner.split_whitespace().enumerate() {
4550+
arr.insert(idx, val.trim_matches('"').to_string());
4551+
}
4552+
}
4553+
} else if flags.nameref {
45094554
self.variables
45104555
.insert(format!("_NAMEREF_{}", var_name), value.to_string());
45114556
} else {
@@ -7136,6 +7181,17 @@ impl Interpreter {
71367181
} else {
71377182
result.push_str(&expanded);
71387183
}
7184+
} else if let Some(&c) = chars.peek()
7185+
&& matches!(c, '#' | '?' | '$' | '!' | '@' | '*' | '-')
7186+
{
7187+
// Handle special variables: $#, $?, $$, $!, $@, $*, $-
7188+
chars.next();
7189+
let value = self.expand_variable(&c.to_string());
7190+
if value.is_empty() {
7191+
result.push('0');
7192+
} else {
7193+
result.push_str(&value);
7194+
}
71397195
} else {
71407196
// Handle $var syntax (common in arithmetic)
71417197
let mut name = String::new();

crates/bashkit/tests/spec_cases/bash/arithmetic.test.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,3 +493,26 @@ echo $x
493493
### expect
494494
42
495495
### end
496+
497+
### arith_special_var_hash
498+
# $# in arithmetic context
499+
set -- a b c
500+
echo "argc: $#"
501+
(( $# > 0 )) && echo "true" || echo "false"
502+
(( 3 > 0 )) && echo "true2" || echo "false2"
503+
x=$#
504+
(( x > 0 )) && echo "true3" || echo "false3"
505+
### expect
506+
argc: 3
507+
true
508+
true2
509+
true3
510+
### end
511+
512+
### arith_special_var_question
513+
# $? in arithmetic context
514+
true
515+
(( $? == 0 )) && echo "zero" || echo "nonzero"
516+
### expect
517+
zero
518+
### end

crates/bashkit/tests/spec_cases/bash/arrays.test.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,26 @@ echo "${arr[@]}"
212212
### expect
213213
10 20 30 40 99
214214
### end
215+
216+
### local_array_compound_assignment
217+
# local arr=(a b c) should initialize the array
218+
myfunc() {
219+
local arr=(one two three)
220+
echo "count: ${#arr[@]}"
221+
echo "values: ${arr[*]}"
222+
}
223+
myfunc
224+
### expect
225+
count: 3
226+
values: one two three
227+
### end
228+
229+
### local_array_compound_in_global
230+
# local arr=(...) at global scope should also work
231+
local arr=(x y z)
232+
echo "${#arr[@]}"
233+
echo "${arr[1]}"
234+
### expect
235+
3
236+
y
237+
### end

crates/bashkit/tests/spec_cases/bash/nameref.test.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ jam
4444

4545
### nameref_local_dynamic_scope
4646
# pass local array by reference via dynamic scoping
47-
### skip: TODO parser does not handle local arr=(...) syntax (indexed array after command name)
47+
### bash_diff: nameref + local array by reference
4848
show_value() {
4949
local -n array_name=$1
5050
local idx=$2

crates/bashkit/tests/spec_tests.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//! - `### skip: reason` - Skip test entirely (not run in any test)
99
//! - `### bash_diff: reason` - Known difference from real bash (runs in spec tests, excluded from comparison)
1010
//!
11-
//! ## Skipped Tests (33 total)
11+
//! ## Skipped Tests (32 total)
1212
//!
1313
//! Actual `### skip:` markers across spec test files:
1414
//!
@@ -24,8 +24,6 @@
2424
//! - [ ] od output format varies
2525
//! - [ ] hexdump -C output format varies
2626
//!
27-
//! ### nameref.test.sh (1 skipped)
28-
//! - [ ] parser does not handle local arr=(...) syntax
2927
//!
3028
//! ### parse-errors.test.sh (6 skipped)
3129
//! - [ ] parser does not reject unexpected 'do' keyword

0 commit comments

Comments
 (0)