rable 0.2.0

A Rust implementation of the Parable bash parser — complete GNU Bash 5.3-compatible parsing with Python bindings
Documentation
# Iteration 23: Case fallthrough (;& and ;;&)

# === Basic fallthrough ;& ===

=== simple fallthrough
case $x in a) echo a;& b) echo b;; esac
---
(case (word "$x") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "b")) (command (word "echo") (word "b"))))
---


=== fallthrough to default
case $x in a) echo a;& *) echo default;; esac
---
(case (word "$x") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "*")) (command (word "echo") (word "default"))))
---


=== multiple fallthrough
case $x in a) echo a;& b) echo b;& c) echo c;; esac
---
(case (word "$x") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "b")) (command (word "echo") (word "b"))) (pattern ((word "c")) (command (word "echo") (word "c"))))
---


=== fallthrough with empty body
case $x in a) ;& b) echo b;; esac
---
(case (word "$x") (pattern ((word "a")) ()) (pattern ((word "b")) (command (word "echo") (word "b"))))
---


# === Continue pattern testing ;;& ===

=== simple continue test
case $x in a) echo a;;& b) echo b;; esac
---
(case (word "$x") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "b")) (command (word "echo") (word "b"))))
---


=== multiple continue test
case $x in a) echo a;;& b) echo b;;& c) echo c;; esac
---
(case (word "$x") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "b")) (command (word "echo") (word "b"))) (pattern ((word "c")) (command (word "echo") (word "c"))))
---


=== continue test with empty body
case $x in a) ;;& b) echo b;; esac
---
(case (word "$x") (pattern ((word "a")) ()) (pattern ((word "b")) (command (word "echo") (word "b"))))
---


# === Mixed terminators ===

=== fallthrough then normal
case $x in a) echo a;& b) echo b;; c) echo c;; esac
---
(case (word "$x") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "b")) (command (word "echo") (word "b"))) (pattern ((word "c")) (command (word "echo") (word "c"))))
---


=== continue test then normal
case $x in a) echo a;;& b) echo b;; c) echo c;; esac
---
(case (word "$x") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "b")) (command (word "echo") (word "b"))) (pattern ((word "c")) (command (word "echo") (word "c"))))
---


=== fallthrough then continue test
case $x in a) echo a;& b) echo b;;& c) echo c;; esac
---
(case (word "$x") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "b")) (command (word "echo") (word "b"))) (pattern ((word "c")) (command (word "echo") (word "c"))))
---


=== continue test then fallthrough
case $x in a) echo a;;& b) echo b;& c) echo c;; esac
---
(case (word "$x") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "b")) (command (word "echo") (word "b"))) (pattern ((word "c")) (command (word "echo") (word "c"))))
---


=== all three terminators mixed
case $x in a) echo a;; b) echo b;& c) echo c;;& d) echo d;; esac
---
(case (word "$x") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "b")) (command (word "echo") (word "b"))) (pattern ((word "c")) (command (word "echo") (word "c"))) (pattern ((word "d")) (command (word "echo") (word "d"))))
---


# === With complex bodies ===

=== fallthrough with pipeline
case $x in a) echo a | cat;& b) echo b;; esac
---
(case (word "$x") (pattern ((word "a")) (pipe (command (word "echo") (word "a")) (command (word "cat")))) (pattern ((word "b")) (command (word "echo") (word "b"))))
---


=== fallthrough with list
case $x in a) echo a && echo b;& c) echo c;; esac
---
(case (word "$x") (pattern ((word "a")) (and (command (word "echo") (word "a")) (command (word "echo") (word "b")))) (pattern ((word "c")) (command (word "echo") (word "c"))))
---


=== fallthrough with multiple commands
case $x in a) echo a; echo b;& c) echo c;; esac
---
(case (word "$x") (pattern ((word "a")) (semi (command (word "echo") (word "a")) (command (word "echo") (word "b")))) (pattern ((word "c")) (command (word "echo") (word "c"))))
---


# === With or patterns ===

=== fallthrough with or pattern
case $x in a|b) echo ab;& c) echo c;; esac
---
(case (word "$x") (pattern ((word "a") (word "b")) (command (word "echo") (word "ab"))) (pattern ((word "c")) (command (word "echo") (word "c"))))
---


=== continue test with or pattern
case $x in a|b) echo ab;;& c|d) echo cd;; esac
---
(case (word "$x") (pattern ((word "a") (word "b")) (command (word "echo") (word "ab"))) (pattern ((word "c") (word "d")) (command (word "echo") (word "cd"))))
---


# === Brutal edge cases ===

=== fallthrough no space
case $x in a) echo a;&b) echo b;; esac
---
(case (word "$x") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "b")) (command (word "echo") (word "b"))))
---


=== continue test no space
case $x in a) echo a;;&b) echo b;; esac
---
(case (word "$x") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "b")) (command (word "echo") (word "b"))))
---


=== fallthrough with newline
case $x in
a) echo a
;&
b) echo b
;; esac
---
(case (word "$x") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "b")) (command (word "echo") (word "b"))))
---


=== continue test with newline
case $x in
a) echo a
;;&
b) echo b
;; esac
---
(case (word "$x") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "b")) (command (word "echo") (word "b"))))
---


=== nested case with fallthrough in outer
case $x in a) case $y in 1) echo 1;; esac;& b) echo b;; esac
---
(case (word "$x") (pattern ((word "a")) (case (word "$y") (pattern ((word "1")) (command (word "echo") (word "1"))))) (pattern ((word "b")) (command (word "echo") (word "b"))))
---


=== nested case with fallthrough in inner
case $x in a) case $y in 1) echo 1;& 2) echo 2;; esac;; esac
---
(case (word "$x") (pattern ((word "a")) (case (word "$y") (pattern ((word "1")) (command (word "echo") (word "1"))) (pattern ((word "2")) (command (word "echo") (word "2"))))))
---


=== glob patterns with fallthrough
case $x in *.txt) echo txt;& *.md) echo md;; esac
---
(case (word "$x") (pattern ((word "*.txt")) (command (word "echo") (word "txt"))) (pattern ((word "*.md")) (command (word "echo") (word "md"))))
---


=== fallthrough in function
f() { case $1 in a) echo a;& b) echo b;; esac; }
---
(function "f" (brace-group (case (word "$1") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "b")) (command (word "echo") (word "b"))))))
---


=== fallthrough with command substitution
x=$(case $y in a) echo a;& b) echo b;; esac)
---
(command (word "x=$(case $y in a)\n        echo a\n    ;&\n    b)\n        echo b\n    ;;\nesac)"))
---


=== fallthrough with subshell body
case $x in a) (echo a);& b) echo b;; esac
---
(case (word "$x") (pattern ((word "a")) (subshell (command (word "echo") (word "a")))) (pattern ((word "b")) (command (word "echo") (word "b"))))
---


=== fallthrough with brace group body
case $x in a) { echo a; };& b) echo b;; esac
---
(case (word "$x") (pattern ((word "a")) (brace-group (command (word "echo") (word "a")))) (pattern ((word "b")) (command (word "echo") (word "b"))))
---


=== multiple pattern match test
case $x in *a*) echo has-a;;& *b*) echo has-b;;& *c*) echo has-c;; esac
---
(case (word "$x") (pattern ((word "*a*")) (command (word "echo") (word "has-a"))) (pattern ((word "*b*")) (command (word "echo") (word "has-b"))) (pattern ((word "*c*")) (command (word "echo") (word "has-c"))))
---


=== fallthrough chain to falltest
case $x in a) echo a;& b) echo b;& c) echo c;;& d) echo d;; esac
---
(case (word "$x") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "b")) (command (word "echo") (word "b"))) (pattern ((word "c")) (command (word "echo") (word "c"))) (pattern ((word "d")) (command (word "echo") (word "d"))))
---


=== default with fallthrough before
case $x in a) echo a;& *) echo default;; esac
---
(case (word "$x") (pattern ((word "a")) (command (word "echo") (word "a"))) (pattern ((word "*")) (command (word "echo") (word "default"))))
---