Skip to content

Commit 865ee37

Browse files
authored
fix(interpreter): handle ++/-- in complex arithmetic expressions (#916)
Closes #903
1 parent fdc38e7 commit 865ee37

File tree

2 files changed

+81
-19
lines changed

2 files changed

+81
-19
lines changed

crates/bashkit/src/interpreter/mod.rs

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2071,34 +2071,63 @@ impl Interpreter {
20712071

20722072
// Handle pre-increment/decrement: ++var or --var
20732073
if let Some(stripped) = expr.strip_prefix("++") {
2074-
let var_name = stripped.trim();
2075-
let current = self.evaluate_arithmetic(var_name);
2076-
let new_value = current + 1;
2077-
self.set_variable(var_name.to_string(), new_value.to_string());
2078-
return new_value;
2074+
let trimmed = stripped.trim_start();
2075+
// Extract the variable name (leading identifier chars)
2076+
let var_end = trimmed
2077+
.find(|c: char| !c.is_ascii_alphanumeric() && c != '_')
2078+
.unwrap_or(trimmed.len());
2079+
let var_name = &trimmed[..var_end];
2080+
if !var_name.is_empty() && is_valid_var_name(var_name) {
2081+
let current = self.evaluate_arithmetic(var_name);
2082+
let new_value = current + 1;
2083+
self.set_variable(var_name.to_string(), new_value.to_string());
2084+
let rest = trimmed[var_end..].trim();
2085+
if rest.is_empty() {
2086+
return new_value;
2087+
}
2088+
// Complex expression: substitute the incremented value and evaluate
2089+
// e.g. "++i > 3" → increment i, then evaluate "1 > 3"
2090+
let full_expr = format!("{new_value}{rest}");
2091+
return self.evaluate_arithmetic(&full_expr);
2092+
}
20792093
}
20802094
if let Some(stripped) = expr.strip_prefix("--") {
2081-
let var_name = stripped.trim();
2082-
let current = self.evaluate_arithmetic(var_name);
2083-
let new_value = current - 1;
2084-
self.set_variable(var_name.to_string(), new_value.to_string());
2085-
return new_value;
2095+
let trimmed = stripped.trim_start();
2096+
let var_end = trimmed
2097+
.find(|c: char| !c.is_ascii_alphanumeric() && c != '_')
2098+
.unwrap_or(trimmed.len());
2099+
let var_name = &trimmed[..var_end];
2100+
if !var_name.is_empty() && is_valid_var_name(var_name) {
2101+
let current = self.evaluate_arithmetic(var_name);
2102+
let new_value = current - 1;
2103+
self.set_variable(var_name.to_string(), new_value.to_string());
2104+
let rest = trimmed[var_end..].trim();
2105+
if rest.is_empty() {
2106+
return new_value;
2107+
}
2108+
let full_expr = format!("{new_value}{rest}");
2109+
return self.evaluate_arithmetic(&full_expr);
2110+
}
20862111
}
20872112

20882113
// Handle post-increment/decrement: var++ or var--
20892114
if let Some(stripped) = expr.strip_suffix("++") {
20902115
let var_name = stripped.trim();
2091-
let current = self.evaluate_arithmetic(var_name);
2092-
let new_value = current + 1;
2093-
self.set_variable(var_name.to_string(), new_value.to_string());
2094-
return current; // Return old value for post-increment
2116+
if is_valid_var_name(var_name) {
2117+
let current = self.evaluate_arithmetic(var_name);
2118+
let new_value = current + 1;
2119+
self.set_variable(var_name.to_string(), new_value.to_string());
2120+
return current; // Return old value for post-increment
2121+
}
20952122
}
20962123
if let Some(stripped) = expr.strip_suffix("--") {
20972124
let var_name = stripped.trim();
2098-
let current = self.evaluate_arithmetic(var_name);
2099-
let new_value = current - 1;
2100-
self.set_variable(var_name.to_string(), new_value.to_string());
2101-
return current; // Return old value for post-decrement
2125+
if is_valid_var_name(var_name) {
2126+
let current = self.evaluate_arithmetic(var_name);
2127+
let new_value = current - 1;
2128+
self.set_variable(var_name.to_string(), new_value.to_string());
2129+
return current; // Return old value for post-decrement
2130+
}
21022131
}
21032132

21042133
// No side effects, just evaluate
@@ -7182,8 +7211,9 @@ impl Interpreter {
71827211
break;
71837212
}
71847213
}
7185-
// Check for array access: name[expr]
7214+
71867215
if chars.peek() == Some(&'[') {
7216+
// Check for array access: name[expr]
71877217
chars.next(); // consume '['
71887218
let mut index_expr = String::new();
71897219
let mut bracket_depth = 1;

crates/bashkit/tests/spec_cases/bash/control-flow.test.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,3 +468,35 @@ echo "1" | select x in a b; do echo "$x"; done
468468
a
469469

470470
### end
471+
472+
### arith_preincrement_and_break
473+
# (( ++i > N )) && break should only break when expression is true
474+
i=0
475+
while true; do
476+
echo "iter $i"
477+
(( ++i > 3 )) && break
478+
done
479+
echo "done"
480+
### expect
481+
iter 0
482+
iter 1
483+
iter 2
484+
iter 3
485+
done
486+
### end
487+
488+
### arith_predecrement_and_break
489+
# (( --i < 0 )) && break should only break when expression is true
490+
i=3
491+
while true; do
492+
echo "iter $i"
493+
(( --i < 0 )) && break
494+
done
495+
echo "done"
496+
### expect
497+
iter 3
498+
iter 2
499+
iter 1
500+
iter 0
501+
done
502+
### end

0 commit comments

Comments
 (0)