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}