rable 0.2.0

A Rust implementation of the Parable bash parser — complete GNU Bash 5.3-compatible parsing with Python bindings
Documentation
# Extglob patterns in case statements
# Extended glob patterns @(), ?(), *(), +(), !() must be handled

=== basic @() extglob
# @extglob
case $x in @(a|b)) echo match;; esac
---
(case (word "$x") (pattern ((word "@(a|b)")) (command (word "echo") (word "match"))))
---


=== basic ?() extglob
# @extglob
case $x in ?(foo|bar)) echo match;; esac
---
(case (word "$x") (pattern ((word "?(foo|bar)")) (command (word "echo") (word "match"))))
---


=== basic *() extglob
# @extglob
case $x in *(a|b|c)) echo match;; esac
---
(case (word "$x") (pattern ((word "*(a|b|c)")) (command (word "echo") (word "match"))))
---


=== basic +() extglob
# @extglob
case $x in +(one|two)) echo match;; esac
---
(case (word "$x") (pattern ((word "+(one|two)")) (command (word "echo") (word "match"))))
---


=== basic !() extglob
# @extglob
case $x in !(bad|worse)) echo good;; esac
---
(case (word "$x") (pattern ((word "!(bad|worse)")) (command (word "echo") (word "good"))))
---


=== extglob single alternative
# @extglob
case $x in @(foo)) echo foo;; esac
---
(case (word "$x") (pattern ((word "@(foo)")) (command (word "echo") (word "foo"))))
---


=== extglob many alternatives
# @extglob
case $x in @(a|b|c|d|e)) echo match;; esac
---
(case (word "$x") (pattern ((word "@(a|b|c|d|e)")) (command (word "echo") (word "match"))))
---


=== nested extglobs
# @extglob
case $x in @(a|@(b|c))) echo match;; esac
---
(case (word "$x") (pattern ((word "@(a|@(b|c))")) (command (word "echo") (word "match"))))
---


=== mixed nested extglobs
# @extglob
case $x in @(?(a)|+(b))) echo match;; esac
---
(case (word "$x") (pattern ((word "@(?(a)|+(b))")) (command (word "echo") (word "match"))))
---


=== extglob with trailing glob
# @extglob
case $x in @(foo|bar)*) echo prefix;; esac
---
(case (word "$x") (pattern ((word "@(foo|bar)*")) (command (word "echo") (word "prefix"))))
---


=== extglob with leading glob
# @extglob
case $x in *@(foo|bar)) echo suffix;; esac
---
(case (word "$x") (pattern ((word "*@(foo|bar)")) (command (word "echo") (word "suffix"))))
---


=== extglob in middle
# @extglob
case $x in pre@(a|b)post) echo match;; esac
---
(case (word "$x") (pattern ((word "pre@(a|b)post")) (command (word "echo") (word "match"))))
---


=== multiple extglobs in alternation
# @extglob
case $x in @(a|b)|@(c|d)) echo match;; esac
---
(case (word "$x") (pattern ((word "@(a|b)") (word "@(c|d)")) (command (word "echo") (word "match"))))
---


=== extglob empty body
# @extglob
case $x in @(a|b));; esac
---
(case (word "$x") (pattern ((word "@(a|b)")) ()))
---


=== multiple clauses with extglobs
# @extglob
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"))))
---


=== extglob with optional leading paren
# @extglob
case $x in (@(a|b)) echo match;; esac
---
(case (word "$x") (pattern ((word "@(a|b)")) (command (word "echo") (word "match"))))
---


=== extglob with wildcards inside
# @extglob
case $x in @(*.txt|*.md)) echo doc;; esac
---
(case (word "$x") (pattern ((word "@(*.txt|*.md)")) (command (word "echo") (word "doc"))))
---


=== extglob with character classes
# @extglob
case $x in @([a-z]*|[0-9]*)) echo match;; esac
---
(case (word "$x") (pattern ((word "@([a-z]*|[0-9]*)")) (command (word "echo") (word "match"))))
---


=== extglob and regular alternation
# @extglob
case $x in foo|@(bar|baz)) echo match;; esac
---
(case (word "$x") (pattern ((word "foo") (word "@(bar|baz)")) (command (word "echo") (word "match"))))
---


=== regular and extglob alternation
# @extglob
case $x in @(foo|bar)|baz) echo match;; esac
---
(case (word "$x") (pattern ((word "@(foo|bar)") (word "baz")) (command (word "echo") (word "match"))))
---


=== deeply nested extglobs
# @extglob
case $x in @(@(@(a)))) echo match;; esac
---
(case (word "$x") (pattern ((word "@(@(@(a)))")) (command (word "echo") (word "match"))))
---


=== command sub inside extglob
# @extglob
case $x in @($(echo a)|b)) echo match;; esac
---
(case (word "$x") (pattern ((word "@($(echo a)|b)")) (command (word "echo") (word "match"))))
---


=== quoted content inside extglob
# @extglob
case $x in @("foo"|bar)) echo match;; esac
---
(case (word "$x") (pattern ((word "@(\"foo\"|bar)")) (command (word "echo") (word "match"))))
---


=== multiple extglobs in pattern
# @extglob
case $x in @(a)*@(b)) echo match;; esac
---
(case (word "$x") (pattern ((word "@(a)*@(b)")) (command (word "echo") (word "match"))))
---


=== escaped paren inside extglob
# @extglob
case $x in @(a\)|b)) echo match;; esac
---
(case (word "$x") (pattern ((word "@(a\\)|b)")) (command (word "echo") (word "match"))))
---


=== arithmetic expansion inside extglob
# @extglob
case $x in @($((1+2)))) echo match;; esac
---
(case (word "$x") (pattern ((word "@($((1+2)))")) (command (word "echo") (word "match"))))
---


=== grouping parens inside extglob
# @extglob
case $x in @((a))) echo match;; esac
---
(case (word "$x") (pattern ((word "@((a))")) (command (word "echo") (word "match"))))
---


=== nested grouping in extglob
# @extglob
case $x in @((a|b)|(c))) echo match;; esac
---
(case (word "$x") (pattern ((word "@((a|b)|(c))")) (command (word "echo") (word "match"))))
---


=== backtick command sub in extglob
# @extglob
case $x in @(`cmd`)) echo match;; esac
---
(case (word "$x") (pattern ((word "@(`cmd`)")) (command (word "echo") (word "match"))))
---