~/s/m/build > cat test.sh
#!/bin/sh
echo $(case x in x) echo foo;; esac)
~/s/m/build > mrsh -n test.sh
program
program
└─command_list ─ pipeline
└─simple_command
├─name ─ word_string [2:1 → 2:5] echo
└─argument 1 ─ word_command ─ program
└─command_list ─ pipeline
└─case_clause
├─word ─ word_string [2:13 → 2:14] x
└─items
└─case_item
├─patterns
│ └─word_string [2:18 → 2:19] x
└─body
└─command_list ─ pipeline
└─simple_command
├─name ─ word_string [2:21 → 2:25] echo
└─argument 1 ─ word_string [2:26 → 2:29] foo
IIRC a case that really seals the deal is:
How do you find the closing paren? You basically have to parse shell, so you might as well do that at parse time rather than runtime. There's a section in the aosabook bash chapter that talks about that.In other words, bash has had parsing bugs with PAREN MATCHING for 20 years (I have a case in my suite that was fixed between bash 4.3 and 4.4). If you just statically parse then you can get it right all on the first try.
It can get arbitrarily complicated, you can add a subshell and nested command subs in there too, etc.:
Bash syntax makes it worse, but this problem appears in POSIX sh too.[1] http://www.oilshell.org/blog/2016/10/13.html