Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions crates/bashkit/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,13 @@ impl<'a> Parser<'a> {
self.advance();
}
Some(tokens::Token::LeftParen) => {
if saw_regex_op {
// Regex pattern starts with '(' — collect it
let pattern = self.collect_conditional_regex_pattern("(");
words.push(Word::literal(&pattern));
saw_regex_op = false;
continue;
}
words.push(Word::literal("("));
self.advance();
}
Expand Down
69 changes: 69 additions & 0 deletions crates/bashkit/tests/spec_cases/bash/control-flow.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,72 @@ ERR
done
BYE
### end

### regex_match_basic
# [[ =~ ]] regex match returns correct exit code
[[ "hello123" =~ [0-9]+ ]]; echo $?
[[ "hello" =~ [0-9]+ ]]; echo $?
### expect
0
1
### end

### regex_match_bash_rematch
# BASH_REMATCH populated with capture groups
x="hello123world"
[[ "$x" =~ ([0-9]+) ]]
echo "${BASH_REMATCH[0]}"
echo "${BASH_REMATCH[1]}"
### expect
123
123
### end

### regex_match_multiple_groups
# Multiple capture groups in BASH_REMATCH
[[ "2024-01-15" =~ ^([0-9]{4})-([0-9]{2})-([0-9]{2})$ ]]
echo "${BASH_REMATCH[0]}"
echo "${BASH_REMATCH[1]}"
echo "${BASH_REMATCH[2]}"
echo "${BASH_REMATCH[3]}"
### expect
2024-01-15
2024
01
15
### end

### regex_match_nested_groups
# Nested capture groups
[[ "foo123bar" =~ (foo([0-9]+)bar) ]]
echo "${BASH_REMATCH[0]}"
echo "${BASH_REMATCH[1]}"
echo "${BASH_REMATCH[2]}"
### expect
foo123bar
foo123bar
123
### end

### regex_match_no_match_clears
# BASH_REMATCH cleared on no match
[[ "abc123" =~ ([0-9]+) ]]
echo "before: ${#BASH_REMATCH[@]}"
[[ "abc" =~ ([0-9]+) ]]
echo "after: ${#BASH_REMATCH[@]}"
### expect
before: 2
after: 0
### end

### regex_match_in_conditional
# Regex match used in && chain
x="error: line 42"
if [[ "$x" =~ error:\ line\ ([0-9]+) ]]; then
echo "line ${BASH_REMATCH[1]}"
else
echo "no match"
fi
### expect
line 42
### end
10 changes: 5 additions & 5 deletions specs/009-implementation-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,16 @@ Bashkit implements IEEE 1003.1-2024 Shell Command Language. See

## Spec Test Coverage

**Total spec test cases:** 1161 (1156 pass, 5 skip)
**Total spec test cases:** 1170 (1165 pass, 5 skip)

| Category | Cases | In CI | Pass | Skip | Notes |
|----------|-------|-------|------|------|-------|
| Bash (core) | 800 | Yes | 795 | 5 | `bash_spec_tests` in CI |
| Bash (core) | 809 | Yes | 804 | 5 | `bash_spec_tests` in CI |
| AWK | 96 | Yes | 96 | 0 | loops, arrays, -v, ternary, field assign, getline, %.6g |
| Grep | 76 | Yes | 76 | 0 | -z, -r, -a, -b, -H, -h, -f, -P, --include, --exclude, binary detect |
| Sed | 75 | Yes | 75 | 0 | hold space, change, regex ranges, -E |
| JQ | 114 | Yes | 114 | 0 | reduce, walk, regex funcs, --arg/--argjson, combined flags, input/inputs, env |
| **Total** | **1161** | **Yes** | **1156** | **5** | |
| **Total** | **1170** | **Yes** | **1165** | **5** | |

### Bash Spec Tests Breakdown

Expand All @@ -128,15 +128,15 @@ Bashkit implements IEEE 1003.1-2024 Shell Command Language. See
| command-not-found.test.sh | 17 | unknown command handling |
| conditional.test.sh | 17 | `[[ ]]` conditionals, `=~` regex, BASH_REMATCH |
| command-subst.test.sh | 14 | includes backtick substitution (1 skipped) |
| control-flow.test.sh | 37 | if/elif/else, for, while, case, trap ERR |
| control-flow.test.sh | 43 | if/elif/else, for, while, case, trap ERR, `[[ =~ ]]` BASH_REMATCH |
| cuttr.test.sh | 32 | cut and tr commands, `-z` zero-terminated |
| date.test.sh | 38 | format specifiers, `-d` relative/compound/epoch, `-R`, `-I`, `%N` (2 skipped) |
| diff.test.sh | 4 | line diffs |
| echo.test.sh | 24 | escape sequences |
| errexit.test.sh | 8 | set -e tests |
| fileops.test.sh | 21 | |
| find.test.sh | 10 | file search |
| functions.test.sh | 14 | |
| functions.test.sh | 17 | local dynamic scoping, nested writes |
| getopts.test.sh | 9 | POSIX option parsing, combined flags, silent mode |
| globs.test.sh | 12 | for-loop glob expansion, recursive `**` |
| headtail.test.sh | 14 | |
Expand Down
Loading