1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//! Shell source to [`ir`](super::ir): a recursive-descent parser for the
//! supported subset (see `docs/analysis.md`).
//!
//! Two promises, both load-bearing for everything downstream:
//!
//! - **Never guess.** A construct outside the subset becomes
//! [`Cmd::Unsupported`](super::ir::Cmd::Unsupported) with its span; text
//! the parser cannot make sense of at all is an [`Err`].
//! - **Delimiters belong to their execution context.** The `do` of a loop
//! inside `$(…)` closes nothing outside it; `do for …` is a body that
//! begins with a `for` command.
//!
//! The parser is scannerless: shell tokenisation depends on where the parser
//! is (`do` is a keyword in command position and a word after `echo`, `)`
//! ends a subshell and a `case` pattern), so one cursor over the original
//! bytes does both jobs. That is also what makes every span — including
//! those inside substitutions, which are parsed in place rather than copied
//! out — an offset into the string the user wrote.
//!
//! A list of one command is that command, not a one-element
//! [`Cmd::Seq`](super::ir::Cmd::Seq); an empty source is an empty `Seq`.
use ;
use Exhausted;
/// Why the source could not be turned into IR.
/// Parse a whole command string.