conch_parser/ast/
mod.rs

1//! Defines abstract representations of the shell source.
2use std::{fmt, ops};
3use std::rc::Rc;
4use std::sync::Arc;
5
6pub mod builder;
7
8/// Type alias for the default `Parameter` representation.
9pub type DefaultParameter = Parameter<String>;
10
11/// Represents reading a parameter (or variable) value, e.g. `$foo`.
12///
13/// Generic over the representation of variable names.
14#[derive(Debug, PartialEq, Eq, Clone)]
15pub enum Parameter<T> {
16    /// $@
17    At,
18    /// $*
19    Star,
20    /// $#
21    Pound,
22    /// $?
23    Question,
24    /// $-
25    Dash,
26    /// $$
27    Dollar,
28    /// $!
29    Bang,
30    /// $0, $1, ..., $9, ${100}
31    Positional(u32),
32    /// $foo
33    Var(T),
34}
35
36/// Type alias for the default `ParameterSubstitution` representation.
37pub type DefaultParameterSubstitution = ParameterSubstitution<
38    DefaultParameter,
39    TopLevelWord<String>,
40    TopLevelCommand<String>,
41    DefaultArithmetic
42>;
43
44/// A parameter substitution, e.g. `${param-word}`.
45///
46/// Generic over the representations of parameters, shell words and
47/// commands, and arithmetic expansions.
48#[derive(Debug, PartialEq, Eq, Clone)]
49pub enum ParameterSubstitution<P, W, C, A> {
50    /// Returns the standard output of running a command, e.g. `$(cmd)`
51    Command(Vec<C>),
52    /// Returns the length of the value of a parameter, e.g. `${#param}`
53    Len(P),
54    /// Returns the resulting value of an arithmetic subsitution, e.g. `$(( x++ ))`
55    Arith(Option<A>),
56    /// Use a provided value if the parameter is null or unset, e.g.
57    /// `${param:-[word]}`.
58    /// The boolean indicates the presence of a `:`, and that if the parameter has
59    /// a null value, that situation should be treated as if the parameter is unset.
60    Default(bool, P, Option<W>),
61    /// Assign a provided value to the parameter if it is null or unset,
62    /// e.g. `${param:=[word]}`.
63    /// The boolean indicates the presence of a `:`, and that if the parameter has
64    /// a null value, that situation should be treated as if the parameter is unset.
65    Assign(bool, P, Option<W>),
66    /// If the parameter is null or unset, an error should result with the provided
67    /// message, e.g. `${param:?[word]}`.
68    /// The boolean indicates the presence of a `:`, and that if the parameter has
69    /// a null value, that situation should be treated as if the parameter is unset.
70    Error(bool, P, Option<W>),
71    /// If the parameter is NOT null or unset, a provided word will be used,
72    /// e.g. `${param:+[word]}`.
73    /// The boolean indicates the presence of a `:`, and that if the parameter has
74    /// a null value, that situation should be treated as if the parameter is unset.
75    Alternative(bool, P, Option<W>),
76    /// Remove smallest suffix pattern from a parameter's value, e.g. `${param%pattern}`
77    RemoveSmallestSuffix(P, Option<W>),
78    /// Remove largest suffix pattern from a parameter's value, e.g. `${param%%pattern}`
79    RemoveLargestSuffix(P, Option<W>),
80    /// Remove smallest prefix pattern from a parameter's value, e.g. `${param#pattern}`
81    RemoveSmallestPrefix(P, Option<W>),
82    /// Remove largest prefix pattern from a parameter's value, e.g. `${param##pattern}`
83    RemoveLargestPrefix(P, Option<W>),
84}
85
86/// A type alias for the default hiearchy for representing shell words.
87pub type ShellWord<T, W, C> = ComplexWord<Word<T, SimpleWord<T, Parameter<T>,
88    Box<ParameterSubstitution<Parameter<T>, W, C, Arithmetic<T>>
89>>>>;
90
91/// Type alias for the default `ComplexWord` representation.
92pub type DefaultComplexWord = ComplexWord<DefaultWord>;
93
94/// Represents whitespace delimited text.
95///
96/// Generic over the representation of a whitespace delimited word.
97#[derive(Debug, PartialEq, Eq, Clone)]
98pub enum ComplexWord<W> {
99    /// Several distinct words concatenated together.
100    Concat(Vec<W>),
101    /// A regular word.
102    Single(W),
103}
104
105/// Type alias for the default `Word` representation.
106pub type DefaultWord = Word<String, DefaultSimpleWord>;
107
108/// Represents whitespace delimited single, double, or non quoted text.
109///
110/// Generic over the representation of single-quoted literals, and non-quoted words.
111#[derive(Debug, PartialEq, Eq, Clone)]
112pub enum Word<L, W> {
113    /// A regular word.
114    Simple(W),
115    /// List of words concatenated within double quotes.
116    DoubleQuoted(Vec<W>),
117    /// List of words concatenated within single quotes. Virtually
118    /// identical as a literal, but makes a distinction between the two.
119    SingleQuoted(L),
120}
121
122/// Type alias for the default `SimpleWord` representation.
123pub type DefaultSimpleWord = SimpleWord<
124    String,
125    DefaultParameter,
126    Box<DefaultParameterSubstitution>
127>;
128
129/// Represents the smallest fragment of any text.
130///
131/// Generic over the representation of a literals, parameters, and substitutions.
132#[derive(Debug, PartialEq, Eq, Clone)]
133pub enum SimpleWord<L, P, S> {
134    /// A non-special literal word.
135    Literal(L),
136    /// A token which normally has a special meaning is treated as a literal
137    /// because it was escaped, typically with a backslash, e.g. `\"`.
138    Escaped(L),
139    /// Access of a value inside a parameter, e.g. `$foo` or `$$`.
140    Param(P),
141    /// A parameter substitution, e.g. `${param-word}`.
142    Subst(S),
143    /// Represents `*`, useful for handling pattern expansions.
144    Star,
145    /// Represents `?`, useful for handling pattern expansions.
146    Question,
147    /// Represents `[`, useful for handling pattern expansions.
148    SquareOpen,
149    /// Represents `]`, useful for handling pattern expansions.
150    SquareClose,
151    /// Represents `~`, useful for handling tilde expansions.
152    Tilde,
153    /// Represents `:`, useful for handling tilde expansions.
154    Colon,
155}
156
157/// Type alias for the default `Redirect` representation.
158pub type DefaultRedirect = Redirect<TopLevelWord<String>>;
159
160/// Represents redirecting a command's file descriptors.
161///
162/// Generic over the representation of a shell word.
163#[derive(Debug, PartialEq, Eq, Clone)]
164pub enum Redirect<W> {
165    /// Open a file for reading, e.g. `[n]< file`.
166    Read(Option<u16>, W),
167    /// Open a file for writing after truncating, e.g. `[n]> file`.
168    Write(Option<u16>, W),
169    /// Open a file for reading and writing, e.g. `[n]<> file`.
170    ReadWrite(Option<u16>, W),
171    /// Open a file for writing, appending to the end, e.g. `[n]>> file`.
172    Append(Option<u16>, W),
173    /// Open a file for writing, failing if the `noclobber` shell option is set, e.g. `[n]>| file`.
174    Clobber(Option<u16>, W),
175    /// Lines contained in the source that should be provided by as input to a file descriptor.
176    Heredoc(Option<u16>, W),
177    /// Duplicate a file descriptor for reading, e.g. `[n]<& [n|-]`.
178    DupRead(Option<u16>, W),
179    /// Duplicate a file descriptor for writing, e.g. `[n]>& [n|-]`.
180    DupWrite(Option<u16>, W),
181}
182
183/// A grouping of guard and body commands.
184#[derive(Debug, PartialEq, Eq, Clone)]
185pub struct GuardBodyPair<C> {
186    /// The guard commands, which if successful, should lead to the
187    /// execution of the body commands.
188    pub guard: Vec<C>,
189    /// The body commands to execute if the guard is successful.
190    pub body: Vec<C>,
191}
192
193/// A grouping of patterns and body commands.
194#[derive(Debug, PartialEq, Eq, Clone)]
195pub struct PatternBodyPair<W, C> {
196    /// Pattern alternatives to match against.
197    pub patterns: Vec<W>,
198    /// The body commands to execute if the pattern matches.
199    pub body: Vec<C>,
200}
201
202/// Type alias for the default `Command` representation.
203pub type DefaultCommand = Command<DefaultAndOrList>;
204
205/// Represents any valid shell command.
206#[derive(Debug, PartialEq, Eq, Clone)]
207pub enum Command<T> {
208    /// A command that runs asynchronously, that is, the shell will not wait
209    /// for it to exit before running the next command, e.g. `foo &`.
210    Job(T),
211    /// A list of and/or commands, e.g. `foo && bar || baz`.
212    List(T),
213}
214
215/// A type alias over an and/or list of conventional shell commands.
216///
217/// Generic over the representation of literals, shell words, commands, and redirects.
218/// Uses `Rc` wrappers around function declarations.
219pub type CommandList<T, W, C> = AndOrList<ListableCommand<ShellPipeableCommand<T, W, C>>>;
220
221/// A type alias over an and/or list of conventional shell commands.
222///
223/// Generic over the representation of literals, shell words, commands, and redirects.
224/// Uses `Arc` wrappers around function declarations.
225pub type AtomicCommandList<T, W, C>
226    = AndOrList<ListableCommand<AtomicShellPipeableCommand<T, W, C>>>;
227
228/// A type alias for the default hiearchy to represent pipeable commands,
229/// using `Rc` wrappers around function declarations.
230pub type ShellPipeableCommand<T, W, C> = PipeableCommand<
231    T,
232    Box<SimpleCommand<T, W, Redirect<W>>>,
233    Box<ShellCompoundCommand<T, W, C>>,
234    Rc<ShellCompoundCommand<T, W, C>>
235>;
236
237/// A type alias for the default hiearchy to represent pipeable commands,
238/// using `Arc` wrappers around function declarations.
239pub type AtomicShellPipeableCommand<T, W, C> = PipeableCommand<
240    T,
241    Box<SimpleCommand<T, W, Redirect<W>>>,
242    Box<ShellCompoundCommand<T, W, C>>,
243    Arc<ShellCompoundCommand<T, W, C>>
244>;
245
246/// A command which conditionally runs based on the exit status of the previous command.
247#[derive(Debug, PartialEq, Eq, Clone)]
248pub enum AndOr<T> {
249    /// A compound command which should run only if the previously run command succeeded.
250    And(T),
251    /// A compound command which should run only if the previously run command failed.
252    Or(T),
253}
254
255/// Type alias for the default `AndOrList` representation.
256pub type DefaultAndOrList = AndOrList<DefaultListableCommand>;
257
258/// A nonempty list of `AndOr` commands, e.g. `foo && bar || baz`.
259#[derive(Debug, PartialEq, Eq, Clone)]
260pub struct AndOrList<T> {
261    /// The first command that always runs.
262    pub first: T,
263    /// The remainder of the conditional commands which may or may not run.
264    pub rest: Vec<AndOr<T>>,
265}
266
267/// Type alias for the default `ListableCommand` representation.
268pub type DefaultListableCommand = ListableCommand<DefaultPipeableCommand>;
269
270/// Commands that can be used within an and/or list.
271#[derive(Debug, PartialEq, Eq, Clone)]
272pub enum ListableCommand<T> {
273    /// A chain of concurrent commands where the standard output of the
274    /// previous becomes the standard input of the next, e.g.
275    /// `[!] foo | bar | baz`.
276    ///
277    /// The bool indicates if a logical negation of the last command's status
278    /// should be returned.
279    Pipe(bool, Vec<T>),
280    /// A single command not part of a pipeline.
281    Single(T),
282}
283
284/// Type alias for the default `PipeableCommand` representation.
285pub type DefaultPipeableCommand = ShellPipeableCommand<
286    String,
287    TopLevelWord<String>,
288    TopLevelCommand<String>
289>;
290
291/// Commands that can be used within a pipeline.
292///
293/// Generic over the representations of function names, simple commands,
294/// compound commands, and function bodies.
295#[derive(Debug, PartialEq, Eq, Clone)]
296pub enum PipeableCommand<N, S, C, F> {
297    /// The simplest possible command: an executable with arguments,
298    /// environment variable assignments, and redirections.
299    Simple(S),
300    /// A class of commands where redirection is applied to a command group.
301    Compound(C),
302    /// A function definition, associating a name with a group of commands,
303    /// e.g. `function foo() { echo foo function; }`.
304    FunctionDef(N, F),
305}
306
307/// A type alias for the default hiearchy for representing compound shell commands.
308pub type ShellCompoundCommand<T, W, C>
309    = CompoundCommand<CompoundCommandKind<T, W, C>, Redirect<W>>;
310
311/// Type alias for the default `CompoundCommandKind` representation.
312pub type DefaultCompoundCommand = ShellCompoundCommand<
313    String,
314    TopLevelWord<String>,
315    TopLevelCommand<String>
316>;
317
318/// A class of commands where redirection is applied to a command group.
319///
320/// Generic over the representation of a type of compound command, and the
321/// representation of a redirect.
322#[derive(Debug, PartialEq, Eq, Clone)]
323pub struct CompoundCommand<T, R> {
324    /// The specific kind of compound command.
325    pub kind: T,
326    /// Any redirections to be applied to the entire compound command
327    pub io: Vec<R>,
328}
329
330/// Type alias for the default `CompoundCommandKind` representation.
331pub type DefaultCompoundCommandKind = CompoundCommandKind<
332    String,
333    TopLevelWord<String>,
334    TopLevelCommand<String>
335>;
336
337/// A specific kind of a `CompoundCommand`.
338///
339/// Generic over the representation of shell words and commands.
340#[derive(Debug, PartialEq, Eq, Clone)]
341pub enum CompoundCommandKind<V, W, C> {
342    /// A group of commands that should be executed in the current environment.
343    Brace(Vec<C>),
344    /// A group of commands that should be executed in a subshell environment.
345    Subshell(Vec<C>),
346    /// A command that executes its body as long as its guard exits successfully.
347    While(GuardBodyPair<C>),
348    /// A command that executes its body as until as its guard exits unsuccessfully.
349    Until(GuardBodyPair<C>),
350    /// A conditional command that runs the respective command branch when a
351    /// certain of the first condition that exits successfully.
352    If {
353        /// A list of conditional branch-body pairs.
354        conditionals: Vec<GuardBodyPair<C>>,
355        /// An else part to run if no other conditional was taken.
356        else_branch: Option<Vec<C>>,
357    },
358    /// A command that binds a variable to a number of provided words and runs
359    /// its body once for each binding.
360    For {
361        /// The variable to bind to each of the specified words.
362        var: V,
363        /// The words to bind to the specified variable one by one.
364        words: Option<Vec<W>>,
365        /// The body to run with the variable binding.
366        body: Vec<C>,
367    },
368    /// A command that behaves much like a `match` statment in Rust, running
369    /// a branch of commands if a specified word matches another literal or
370    /// glob pattern.
371    Case {
372        /// The word on which to check for pattern matches.
373        word: W,
374        /// The arms to match against.
375        arms: Vec<PatternBodyPair<W, C>>,
376    },
377}
378
379/// Represents a parsed redirect or a defined environment variable at the start
380/// of a command.
381///
382/// Because the order in which redirects are defined may be significant for
383/// execution, the parser will preserve the order in which they were parsed.
384/// Thus we need a wrapper like this to disambiguate what was encountered in
385/// the source program.
386#[derive(Debug, PartialEq, Eq, Clone)]
387pub enum RedirectOrEnvVar<R, V, W> {
388    /// A parsed redirect before a command was encountered.
389    Redirect(R),
390    /// A parsed environment variable, e.g. `foo=[bar]`.
391    EnvVar(V, Option<W>),
392}
393
394/// Represents a parsed redirect or a defined command or command argument.
395///
396/// Because the order in which redirects are defined may be significant for
397/// execution, the parser will preserve the order in which they were parsed.
398/// Thus we need a wrapper like this to disambiguate what was encountered in
399/// the source program.
400#[derive(Debug, PartialEq, Eq, Clone)]
401pub enum RedirectOrCmdWord<R, W> {
402    /// A parsed redirect after a command was encountered.
403    Redirect(R),
404    /// A parsed command name or argument.
405    CmdWord(W),
406}
407
408/// Type alias for the default `SimpleCommand` representation.
409pub type DefaultSimpleCommand = SimpleCommand<
410    String,
411    TopLevelWord<String>,
412    Redirect<TopLevelWord<String>>
413>;
414
415/// The simplest possible command: an executable with arguments,
416/// environment variable assignments, and redirections.
417///
418/// Generic over representations of variable names, shell words, and redirects.
419#[derive(Debug, PartialEq, Eq, Clone)]
420pub struct SimpleCommand<V, W, R> {
421    /// Redirections or environment variables that occur before any command
422    /// in the order they were parsed.
423    pub redirects_or_env_vars: Vec<RedirectOrEnvVar<R, V, W>>,
424    /// Redirections or command name/argumetns in the order they were parsed.
425    pub redirects_or_cmd_words: Vec<RedirectOrCmdWord<R, W>>,
426}
427
428/// Type alias for the default `Arithmetic` representation.
429pub type DefaultArithmetic = Arithmetic<String>;
430
431/// Represents an expression within an arithmetic subsitution.
432///
433/// Generic over the representation of a variable name.
434#[derive(Debug, PartialEq, Eq, Clone)]
435pub enum Arithmetic<T> {
436    /// The value of a variable, e.g. `$var` or `var`.
437    Var(T),
438    /// A numeric literal such as `42` or `0xdeadbeef`.
439    Literal(isize),
440    /// `left ** right`.
441    Pow(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
442    /// Returns the current value of a variable,
443    /// and then increments its value immediately after, e.g. `var++`
444    PostIncr(T),
445    /// Returns the current value of a variable,
446    /// and then decrements its value immediately after, e.g. `var--`
447    PostDecr(T),
448    /// Increments the value of a variable and returns the new value, e.g. `++var`.
449    PreIncr(T),
450    /// Decrements the value of a variable and returns the new value, e.g. `--var`.
451    PreDecr(T),
452    /// Ensures the sign of the underlying result is positive, e.g. `+(1-2)`.
453    UnaryPlus(Box<Arithmetic<T>>),
454    /// Ensures the sign of the underlying result is negative, e.g. `-(1+2)`.
455    UnaryMinus(Box<Arithmetic<T>>),
456    /// Returns one if the underlying result is zero, or zero otherwise, e.g. `!expr`.
457    LogicalNot(Box<Arithmetic<T>>),
458    /// Flips all bits from the underlying result, e.g. `~expr`.
459    BitwiseNot(Box<Arithmetic<T>>),
460    /// `left * right`
461    Mult(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
462    /// `left / right`
463    Div(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
464    /// `left % right`
465    Modulo(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
466    /// `left + right`
467    Add(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
468    /// `left - right`
469    Sub(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
470    /// `left << right`
471    ShiftLeft(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
472    /// `left >> right`
473    ShiftRight(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
474    /// `left < right`
475    Less(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
476    /// `left <= right`
477    LessEq(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
478    /// `left > right`
479    Great(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
480    /// `left >= right`
481    GreatEq(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
482    /// `left == right`
483    Eq(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
484    /// `left != right`
485    NotEq(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
486    /// `left & right`
487    BitwiseAnd(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
488    /// `left ^ right`
489    BitwiseXor(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
490    /// `left | right`
491    BitwiseOr(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
492    /// `left && right`
493    LogicalAnd(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
494    /// `left || right`
495    LogicalOr(Box<Arithmetic<T>>, Box<Arithmetic<T>>),
496    /// `first ? second : third`
497    Ternary(Box<Arithmetic<T>>, Box<Arithmetic<T>>, Box<Arithmetic<T>>),
498    /// Assigns the value of an underlying expression to a
499    /// variable and returns the value, e.g. `x = 5`, or `x += 2`.
500    Assign(T, Box<Arithmetic<T>>),
501    /// `expr[, expr[, ...]]`
502    Sequence(Vec<Arithmetic<T>>),
503}
504
505macro_rules! impl_top_level_cmd {
506    ($(#[$attr:meta])* pub struct $Cmd:ident, $CmdList:ident, $Word:ident) => {
507        $(#[$attr])*
508        #[derive(Debug, PartialEq, Eq, Clone)]
509        pub struct $Cmd<T>(pub Command<$CmdList<T, $Word<T>, $Cmd<T>>>);
510
511        impl<T> ops::Deref for $Cmd<T> {
512            type Target = Command<$CmdList<T, $Word<T>, $Cmd<T>>>;
513
514            fn deref(&self) -> &Self::Target {
515                &self.0
516            }
517        }
518
519        impl<T> ops::DerefMut for $Cmd<T> {
520            fn deref_mut(&mut self) -> &mut Self::Target {
521                &mut self.0
522            }
523        }
524
525        impl<T> PartialEq<Command<$CmdList<T, $Word<T>, $Cmd<T>>>> for $Cmd<T> where T: PartialEq<T>
526        {
527            fn eq(&self, other: &Command<$CmdList<T, $Word<T>, $Cmd<T>>>) -> bool {
528                &self.0 == other
529            }
530        }
531
532        impl<T> From<Command<$CmdList<T, $Word<T>, $Cmd<T>>>> for $Cmd<T> {
533            fn from(inner: Command<$CmdList<T, $Word<T>, $Cmd<T>>>) -> Self {
534                $Cmd(inner)
535            }
536        }
537    };
538}
539
540impl_top_level_cmd! {
541    /// A top-level representation of a shell command. Uses `Rc` wrappers for function declarations.
542    ///
543    /// This wrapper unifies the provided top-level word representation,
544    /// `ComplexWord`, and the top-level command representation, `Command`,
545    /// while allowing them to be generic on their own.
546    pub struct TopLevelCommand,
547    CommandList,
548    TopLevelWord
549}
550
551impl_top_level_cmd! {
552    /// A top-level representation of a shell command. Uses `Arc` wrappers for function declarations.
553    ///
554    /// This wrapper unifies the provided top-level word representation,
555    /// `ComplexWord`, and the top-level command representation, `Command`,
556    /// while allowing them to be generic on their own.
557    pub struct AtomicTopLevelCommand,
558    AtomicCommandList,
559    AtomicTopLevelWord
560}
561
562macro_rules! impl_top_level_word {
563    ($(#[$attr:meta])* pub struct $Word:ident, $Cmd:ident) => {
564        $(#[$attr])*
565        #[derive(Debug, PartialEq, Eq, Clone)]
566        pub struct $Word<T>(pub ShellWord<T, $Word<T>, $Cmd<T>>);
567
568        impl<T> ops::Deref for $Word<T> {
569            type Target = ShellWord<T, $Word<T>, $Cmd<T>>;
570
571            fn deref(&self) -> &Self::Target {
572                &self.0
573            }
574        }
575
576        impl<T> ops::DerefMut for $Word<T> {
577            fn deref_mut(&mut self) -> &mut Self::Target {
578                &mut self.0
579            }
580        }
581
582        impl<T> PartialEq<ShellWord<T, $Word<T>, $Cmd<T>>> for $Word<T> where T: PartialEq<T> {
583            fn eq(&self, other: &ShellWord<T, $Word<T>, $Cmd<T>>) -> bool {
584                &self.0 == other
585            }
586        }
587
588        impl<T> From<ShellWord<T, $Word<T>, $Cmd<T>>> for $Word<T> {
589            fn from(inner: ShellWord<T, $Word<T>, $Cmd<T>>) -> Self {
590                $Word(inner)
591            }
592        }
593    };
594}
595
596impl_top_level_word! {
597    /// A top-level representation of a shell word. Uses `Rc` wrappers for function declarations.
598    ///
599    /// This wrapper unifies the provided top-level word representation,
600    /// `ComplexWord`, and the top-level command representation, `Command`,
601    /// while allowing them to be generic on their own.
602    pub struct TopLevelWord,
603    TopLevelCommand
604}
605
606impl_top_level_word! {
607    /// A top-level representation of a shell word. Uses `Arc` wrappers for function declarations.
608    ///
609    /// This wrapper unifies the provided top-level word representation,
610    /// `ComplexWord`, and the top-level command representation, `Command`,
611    /// while allowing them to be generic on their own.
612    pub struct AtomicTopLevelWord,
613    AtomicTopLevelCommand
614}
615
616impl<T: fmt::Display> fmt::Display for Parameter<T> {
617    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
618        use self::Parameter::*;
619
620        match *self {
621            At       => fmt.write_str("$@"),
622            Star     => fmt.write_str("$*"),
623            Pound    => fmt.write_str("$#"),
624            Question => fmt.write_str("$?"),
625            Dash     => fmt.write_str("$-"),
626            Dollar   => fmt.write_str("$$"),
627            Bang     => fmt.write_str("$!"),
628
629            Var(ref p)    => write!(fmt, "${{{}}}", p),
630            Positional(p) => if p <= 9 {
631                write!(fmt, "${}", p)
632            } else {
633                write!(fmt, "${{{}}}", p)
634            },
635        }
636    }
637}
638
639#[cfg(test)]
640mod tests {
641    #[test]
642    fn test_display_parameter() {
643        use lexer::Lexer;
644        use parse::DefaultParser;
645        use super::Parameter::*;
646        use super::ComplexWord::Single;
647        use super::SimpleWord::Param;
648        use super::TopLevelWord;
649        use super::Word::Simple;
650
651        let params = vec!(
652            At,
653            Star,
654            Pound,
655            Question,
656            Dash,
657            Dollar,
658            Bang,
659            Positional(0),
660            Positional(10),
661            Positional(100),
662            Var(String::from("foo_bar123")),
663        );
664
665        for p in params {
666            let src = p.to_string();
667            let correct = TopLevelWord(Single(Simple(Param(p))));
668
669            let parsed = match DefaultParser::new(Lexer::new(src.chars())).word() {
670                Ok(Some(w)) => w,
671                Ok(None) => panic!("The source \"{}\" generated from the command `{:#?}` failed to parse as anything", src, correct),
672                Err(e) => panic!("The source \"{}\" generated from the command `{:#?}` failed to parse: {}", src, correct, e),
673            };
674
675            if correct != parsed {
676                panic!("The source \"{}\" generated from the command `{:#?}` was parsed as `{:#?}`", src, correct, parsed);
677            }
678        }
679    }
680}