conch_parser/
token.rs

1//! This module defines the tokens of the shell language.
2
3use std::fmt;
4use self::Token::*;
5
6/// The inner representation of a positional parameter.
7#[derive(PartialEq, Eq, Debug, Clone, Copy)]
8pub enum Positional {
9    /// $0
10    Zero,
11    /// $1
12    One,
13    /// $2
14    Two,
15    /// $3
16    Three,
17    /// $4
18    Four,
19    /// $5
20    Five,
21    /// $6
22    Six,
23    /// $7
24    Seven,
25    /// $8
26    Eight,
27    /// $9
28    Nine,
29}
30
31impl Positional {
32    /// Converts a `Positional` as a numeric representation
33    pub fn as_num(&self) -> u8 {
34        match *self {
35            Positional::Zero  => 0,
36            Positional::One   => 1,
37            Positional::Two   => 2,
38            Positional::Three => 3,
39            Positional::Four  => 4,
40            Positional::Five  => 5,
41            Positional::Six   => 6,
42            Positional::Seven => 7,
43            Positional::Eight => 8,
44            Positional::Nine  => 9,
45        }
46    }
47
48    /// Attempts to convert a number to a `Positional` representation
49    pub fn from_num(num: u8) -> Option<Self> {
50        match num {
51            0 => Some(Positional::Zero),
52            1 => Some(Positional::One),
53            2 => Some(Positional::Two),
54            3 => Some(Positional::Three),
55            4 => Some(Positional::Four),
56            5 => Some(Positional::Five),
57            6 => Some(Positional::Six),
58            7 => Some(Positional::Seven),
59            8 => Some(Positional::Eight),
60            9 => Some(Positional::Nine),
61            _ => None,
62        }
63    }
64}
65
66impl Into<u8> for Positional {
67    fn into(self) -> u8 {
68        self.as_num()
69    }
70}
71
72/// The representation of (context free) shell tokens.
73#[derive(PartialEq, Eq, Debug, Clone)]
74pub enum Token {
75    /// \n
76    Newline,
77
78    /// (
79    ParenOpen,
80    /// )
81    ParenClose,
82    /// {
83    CurlyOpen,
84    /// }
85    CurlyClose,
86    /// [
87    SquareOpen,
88    /// ]
89    SquareClose,
90
91    /// !
92    Bang,
93    /// ~
94    Tilde,
95    /// \#
96    Pound,
97    /// *
98    Star,
99    /// ?
100    Question,
101    /// \\
102    Backslash,
103    /// %
104    Percent,
105    /// \-
106    Dash,
107    /// \=
108    Equals,
109    /// +
110    Plus,
111    /// :
112    Colon,
113    /// @
114    At,
115    /// ^
116    Caret,
117    /// /
118    Slash,
119    /// ,
120    Comma,
121
122    /// '
123    SingleQuote,
124    /// "
125    DoubleQuote,
126    /// `
127    Backtick,
128
129    /// ;
130    Semi,
131    /// &
132    Amp,
133    /// |
134    Pipe,
135    /// &&
136    AndIf,
137    /// ||
138    OrIf,
139    /// ;;
140    DSemi,
141
142    /// <
143    Less,
144    /// \>
145    Great,
146    /// <<
147    DLess,
148    /// \>>
149    DGreat,
150    /// \>&
151    GreatAnd,
152    /// <&
153    LessAnd,
154    /// <<-
155    DLessDash,
156    /// \>|
157    Clobber,
158    /// <>
159    LessGreat,
160
161    /// $
162    Dollar,
163    /// $0, $1, ..., $9
164    ///
165    /// Must be its own token to avoid lumping the positional parameter
166    /// as a `Literal` if the parameter is concatenated to something.
167    ParamPositional(Positional),
168
169    /// Any string of whitespace characters NOT including a newline.
170    Whitespace(String),
171
172    /// Any literal delimited by whitespace.
173    Literal(String),
174    /// A `Literal` capable of being used as a variable or function name. According to the POSIX
175    /// standard it should only contain alphanumerics or underscores, and does not start with a digit.
176    Name(String),
177}
178
179impl fmt::Display for Token {
180    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
181        write!(fmt, "{}", self.as_str())
182    }
183}
184
185impl Token {
186    /// Returns if the token's length is zero.
187    pub fn is_empty(&self) -> bool {
188        self.len() == 0
189    }
190
191    /// Returns the number of characters it took to recognize a token.
192    pub fn len(&self) -> usize {
193        self.as_str().len()
194    }
195
196    /// Indicates whether a word can be delimited by this token
197    /// when the token is **not** quoted or escaped.
198    pub fn is_word_delimiter(&self) -> bool {
199        match *self {
200            Newline           |
201            ParenOpen         |
202            ParenClose        |
203            Semi              |
204            Amp               |
205            Less              |
206            Great             |
207            Pipe              |
208            AndIf             |
209            OrIf              |
210            DSemi             |
211            DLess             |
212            DGreat            |
213            GreatAnd          |
214            LessAnd           |
215            DLessDash         |
216            Clobber           |
217            LessGreat         |
218            Whitespace(_) => true,
219
220            Bang               |
221            Star               |
222            Question           |
223            Backslash          |
224            SingleQuote        |
225            DoubleQuote        |
226            Backtick           |
227            Percent            |
228            Dash               |
229            Equals             |
230            Plus               |
231            Colon              |
232            At                 |
233            Caret              |
234            Slash              |
235            Comma              |
236            CurlyOpen          |
237            CurlyClose         |
238            SquareOpen         |
239            SquareClose        |
240            Dollar             |
241            Tilde              |
242            Pound              |
243            Name(_)            |
244            Literal(_)         |
245            ParamPositional(_) => false,
246        }
247    }
248
249    /// Gets a representation of the token as a string slice.
250    pub fn as_str(&self) -> &str {
251        match *self {
252            Newline     => "\n",
253            ParenOpen   => "(",
254            ParenClose  => ")",
255            CurlyOpen   => "{",
256            CurlyClose  => "}",
257            SquareOpen  => "[",
258            SquareClose => "]",
259            Dollar      => "$",
260            Bang        => "!",
261            Semi        => ";",
262            Amp         => "&",
263            Less        => "<",
264            Great       => ">",
265            Pipe        => "|",
266            Tilde       => "~",
267            Pound       => "#",
268            Star        => "*",
269            Question    => "?",
270            Backslash   => "\\",
271            Percent     => "%",
272            Dash        => "-",
273            Equals      => "=",
274            Plus        => "+",
275            Colon       => ":",
276            At          => "@",
277            Caret       => "^",
278            Slash       => "/",
279            Comma       => ",",
280            SingleQuote => "\'",
281            DoubleQuote => "\"",
282            Backtick    => "`",
283            AndIf       => "&&",
284            OrIf        => "||",
285            DSemi       => ";;",
286            DLess       => "<<",
287            DGreat      => ">>",
288            GreatAnd    => ">&",
289            LessAnd     => "<&",
290            DLessDash   => "<<-",
291            Clobber     => ">|",
292            LessGreat   => "<>",
293
294            ParamPositional(Positional::Zero)  => "$0",
295            ParamPositional(Positional::One)   => "$1",
296            ParamPositional(Positional::Two)   => "$2",
297            ParamPositional(Positional::Three) => "$3",
298            ParamPositional(Positional::Four)  => "$4",
299            ParamPositional(Positional::Five)  => "$5",
300            ParamPositional(Positional::Six)   => "$6",
301            ParamPositional(Positional::Seven) => "$7",
302            ParamPositional(Positional::Eight) => "$8",
303            ParamPositional(Positional::Nine)  => "$9",
304
305            Whitespace(ref s) |
306            Name(ref s)       |
307            Literal(ref s)    => s,
308        }
309    }
310}