Skip to main content

yash_arith/
token.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2022 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! Tokenization
18
19use std::fmt::Display;
20use std::iter::FusedIterator;
21use std::ops::Range;
22use thiserror::Error;
23
24/// Result of evaluating an expression
25///
26/// TODO: The current implementation only supports integer arithmetic. A future
27/// version may also support floating-point numbers.
28#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
29#[non_exhaustive]
30pub enum Value {
31    Integer(i64),
32}
33
34impl Display for Value {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        match self {
37            Value::Integer(i) => i.fmt(f),
38        }
39    }
40}
41
42/// Intermediate result of evaluating part of an expression
43#[derive(Clone, Debug, Eq, Hash, PartialEq)]
44pub enum Term<'a> {
45    /// Value
46    Value(Value),
47    /// Variable
48    Variable {
49        /// Variable name
50        name: &'a str,
51        /// Range of the substring in the evaluated expression where the variable occurs
52        location: Range<usize>,
53    },
54}
55
56/// Operator
57#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
58pub enum Operator {
59    /// `?`
60    Question,
61    /// `:`
62    Colon,
63    /// `|`
64    Bar,
65    /// `||`
66    BarBar,
67    /// `|=`
68    BarEqual,
69    /// `^`
70    Caret,
71    /// `^=`
72    CaretEqual,
73    /// `&`
74    And,
75    /// `&&`
76    AndAnd,
77    /// `&=`
78    AndEqual,
79    /// `=`
80    Equal,
81    /// `==`
82    EqualEqual,
83    /// `!`
84    Bang,
85    /// `!=`
86    BangEqual,
87    /// `<`
88    Less,
89    /// `<=`
90    LessEqual,
91    /// `<<`
92    LessLess,
93    /// `<<=`
94    LessLessEqual,
95    /// `>`
96    Greater,
97    /// `>=`
98    GreaterEqual,
99    /// `>>`
100    GreaterGreater,
101    /// `>>=`
102    GreaterGreaterEqual,
103    /// `+`
104    Plus,
105    /// `++`
106    PlusPlus,
107    /// `+=`
108    PlusEqual,
109    /// `-`
110    Minus,
111    /// `--`
112    MinusMinus,
113    /// `-=`
114    MinusEqual,
115    /// `*`
116    Asterisk,
117    /// `*=`
118    AsteriskEqual,
119    /// `/`
120    Slash,
121    /// `/=`
122    SlashEqual,
123    /// `%`
124    Percent,
125    /// `%=`
126    PercentEqual,
127    /// `~`
128    Tilde,
129    /// `(`
130    OpenParen,
131    /// `)`
132    CloseParen,
133}
134
135/// Value of a [`Token`].
136#[derive(Clone, Debug, Eq, Hash, PartialEq)]
137pub enum TokenValue<'a> {
138    /// Term
139    Term(Term<'a>),
140    /// Operator
141    Operator(Operator),
142    /// Imaginary token value for the end of input.
143    EndOfInput,
144}
145
146/// Atomic lexical element of an expression
147#[derive(Clone, Debug, Eq, Hash, PartialEq)]
148pub struct Token<'a> {
149    /// Token value
150    pub value: TokenValue<'a>,
151    /// Range of the substring where the token occurs in the parsed expression
152    pub location: Range<usize>,
153}
154
155/// Cause of a tokenization error
156#[derive(Clone, Debug, Eq, Error, Hash, PartialEq)]
157#[non_exhaustive]
158pub enum TokenError {
159    /// A value token contains an invalid character.
160    #[error("invalid numeric constant")]
161    InvalidNumericConstant,
162
163    /// An expression contains a character that is not a whitespace, operator,
164    /// or number.
165    #[error("invalid character")]
166    InvalidCharacter,
167}
168
169/// Description of an error that occurred during expansion
170#[derive(Clone, Debug, Eq, Hash, PartialEq)]
171pub struct Error {
172    /// Cause of the error
173    pub cause: TokenError,
174    /// Range of the substring in the evaluated expression string where the error occurred
175    pub location: Range<usize>,
176}
177
178/// List of all the operators.
179///
180/// If a prefix of a valid operator is another operator, the prefix (the shorter
181/// operator) must appear after the longer. With this ordering, we can
182/// short-circuit unnecessary matching on finding a first match.
183const OPERATORS: &[(&str, Operator)] = &[
184    ("?", Operator::Question),
185    (":", Operator::Colon),
186    ("|=", Operator::BarEqual),
187    ("||", Operator::BarBar),
188    ("|", Operator::Bar),
189    ("^=", Operator::CaretEqual),
190    ("^", Operator::Caret),
191    ("&=", Operator::AndEqual),
192    ("&&", Operator::AndAnd),
193    ("&", Operator::And),
194    ("==", Operator::EqualEqual),
195    ("=", Operator::Equal),
196    ("!=", Operator::BangEqual),
197    ("<=", Operator::LessEqual),
198    ("<<=", Operator::LessLessEqual),
199    ("<<", Operator::LessLess),
200    ("<", Operator::Less),
201    (">=", Operator::GreaterEqual),
202    (">>=", Operator::GreaterGreaterEqual),
203    (">>", Operator::GreaterGreater),
204    (">", Operator::Greater),
205    ("+=", Operator::PlusEqual),
206    ("++", Operator::PlusPlus),
207    ("+", Operator::Plus),
208    ("-=", Operator::MinusEqual),
209    ("--", Operator::MinusMinus),
210    ("-", Operator::Minus),
211    ("*=", Operator::AsteriskEqual),
212    ("*", Operator::Asterisk),
213    ("/=", Operator::SlashEqual),
214    ("/", Operator::Slash),
215    ("%=", Operator::PercentEqual),
216    ("%", Operator::Percent),
217    ("~", Operator::Tilde),
218    ("!", Operator::Bang),
219    ("(", Operator::OpenParen),
220    (")", Operator::CloseParen),
221];
222
223/// Iterator extracting tokens from a string
224///
225/// `Tokens` implements `Iterator` but never yields `None` because it returns a
226/// special token with `TokenValue::EndOfInput` when there are no more tokens.
227/// The `next_token` inherent method may be handier than the methods of
228/// `Iterator` since it returns tokens without wrapping them in `Option`.
229///
230/// See also [`PeekableTokens`], which makes the iterator peekable.
231#[derive(Clone, Debug, Eq, Hash, PartialEq)]
232pub struct Tokens<'a> {
233    source: &'a str,
234    index: usize,
235}
236
237impl<'a> Tokens<'a> {
238    /// Creates a tokenizer.
239    pub fn new(source: &'a str) -> Self {
240        Tokens { source, index: 0 }
241    }
242
243    pub fn next_token(&mut self) -> Result<Token<'a>, Error> {
244        let source = self.source[self.index..].trim_start();
245        let start_of_token = self.source.len() - source.len();
246        let first_char = if let Some(c) = source.chars().next() {
247            c
248        } else {
249            return Ok(Token {
250                value: TokenValue::EndOfInput,
251                location: start_of_token..start_of_token,
252            });
253        };
254
255        if let Some((lexeme, operator)) = OPERATORS
256            .iter()
257            .copied()
258            .find(|&(lexeme, _)| source.starts_with(lexeme))
259        {
260            // Okay, this is an operator.
261            let end_of_token = start_of_token + lexeme.len();
262            let location = start_of_token..end_of_token;
263            self.index = end_of_token;
264            Ok(Token {
265                value: TokenValue::Operator(operator),
266                location,
267            })
268        } else {
269            // The next token should be a term. Try parsing it.
270            let remainder = source.trim_start_matches(|c: char| c.is_alphanumeric() || c == '_');
271            let token_len = source.len() - remainder.len();
272            if token_len == 0 {
273                return Err(Error {
274                    cause: TokenError::InvalidCharacter,
275                    location: start_of_token..start_of_token + 1,
276                });
277            }
278            let end_of_token = start_of_token + token_len;
279            let location = start_of_token..end_of_token;
280            let token = &source[..token_len];
281            let term = if first_char.is_ascii_digit() {
282                let parse = if let Some(token_source) = token.strip_prefix("0X") {
283                    i64::from_str_radix(token_source, 0x10)
284                } else if let Some(token_source) = token.strip_prefix("0x") {
285                    i64::from_str_radix(token_source, 0x10)
286                } else if source.starts_with('0') {
287                    i64::from_str_radix(token, 0o10)
288                } else {
289                    token.parse()
290                };
291                match parse {
292                    Ok(i) => Term::Value(Value::Integer(i)),
293                    Err(_) => {
294                        return Err(Error {
295                            cause: TokenError::InvalidNumericConstant,
296                            location,
297                        });
298                    }
299                }
300            } else {
301                Term::Variable {
302                    name: token,
303                    location: location.clone(),
304                }
305            };
306
307            self.index = end_of_token;
308            Ok(Token {
309                value: TokenValue::Term(term),
310                location,
311            })
312        }
313    }
314}
315
316impl<'a> Iterator for Tokens<'a> {
317    type Item = Result<Token<'a>, Error>;
318
319    fn next(&mut self) -> Option<Result<Token<'a>, Error>> {
320        Some(self.next_token())
321    }
322}
323
324/// `Tokens` is fused because it never yields `None`.
325impl FusedIterator for Tokens<'_> {}
326
327/// Peekable iterator extracting tokens from a string
328///
329/// `PeekableTokens` works as a wrapper of [`Tokens`] that adds the
330/// [`peek`](Self::peek) method.
331#[derive(Clone, Debug, Eq, Hash, PartialEq)]
332pub struct PeekableTokens<'a> {
333    inner: Tokens<'a>,
334    cached_next: Option<Result<Token<'a>, Error>>,
335}
336
337impl<'a> PeekableTokens<'a> {
338    /// Creates a tokenizer.
339    pub fn new(inner: Tokens<'a>) -> Self {
340        let cached_next = None;
341        PeekableTokens { inner, cached_next }
342    }
343
344    /// Consumes and returns the next token.
345    pub fn next(&mut self) -> Result<Token<'a>, Error> {
346        self.cached_next
347            .take()
348            .unwrap_or_else(|| self.inner.next_token())
349    }
350
351    /// Returns the next token without consuming it.
352    ///
353    /// The token will be returned again on a next call to `peek` or
354    /// [`next`](Self::next).
355    pub fn peek(&mut self) -> &Result<Token<'a>, Error> {
356        self.cached_next
357            .get_or_insert_with(|| self.inner.next_token())
358    }
359}
360
361impl<'a> From<&'a str> for PeekableTokens<'a> {
362    fn from(source: &'a str) -> Self {
363        PeekableTokens::new(Tokens::new(source))
364    }
365}
366
367#[cfg(test)]
368mod tests {
369    use super::*;
370
371    #[test]
372    fn decimal_integer_constants() {
373        assert_eq!(
374            Tokens::new("1").next(),
375            Some(Ok(Token {
376                value: TokenValue::Term(Term::Value(Value::Integer(1))),
377                location: 0..1,
378            }))
379        );
380        assert_eq!(
381            Tokens::new("42").next(),
382            Some(Ok(Token {
383                value: TokenValue::Term(Term::Value(Value::Integer(42))),
384                location: 0..2,
385            }))
386        );
387    }
388
389    #[test]
390    fn invalid_digit_in_decimal_constant() {
391        assert_eq!(
392            Tokens::new("1a").next(),
393            Some(Err(Error {
394                cause: TokenError::InvalidNumericConstant,
395                location: 0..2,
396            }))
397        );
398        assert_eq!(
399            Tokens::new("  123_456 ").next(),
400            Some(Err(Error {
401                cause: TokenError::InvalidNumericConstant,
402                location: 2..9,
403            }))
404        );
405    }
406
407    #[test]
408    fn octal_integer_constants() {
409        assert_eq!(
410            Tokens::new("0").next(),
411            Some(Ok(Token {
412                value: TokenValue::Term(Term::Value(Value::Integer(0))),
413                location: 0..1,
414            }))
415        );
416        assert_eq!(
417            Tokens::new("01").next(),
418            Some(Ok(Token {
419                value: TokenValue::Term(Term::Value(Value::Integer(0o1))),
420                location: 0..2,
421            }))
422        );
423        assert_eq!(
424            Tokens::new("07").next(),
425            Some(Ok(Token {
426                value: TokenValue::Term(Term::Value(Value::Integer(0o7))),
427                location: 0..2,
428            }))
429        );
430        assert_eq!(
431            Tokens::new("0123").next(),
432            Some(Ok(Token {
433                value: TokenValue::Term(Term::Value(Value::Integer(0o123))),
434                location: 0..4,
435            }))
436        );
437    }
438
439    #[test]
440    fn invalid_digit_in_octal_constant() {
441        assert_eq!(
442            Tokens::new("08").next(),
443            Some(Err(Error {
444                cause: TokenError::InvalidNumericConstant,
445                location: 0..2,
446            }))
447        );
448        assert_eq!(
449            Tokens::new(" 0192 ").next(),
450            Some(Err(Error {
451                cause: TokenError::InvalidNumericConstant,
452                location: 1..5,
453            }))
454        );
455        assert_eq!(
456            Tokens::new("0ab").next(),
457            Some(Err(Error {
458                cause: TokenError::InvalidNumericConstant,
459                location: 0..3,
460            }))
461        );
462    }
463
464    #[test]
465    fn hexadecimal_integer_constants() {
466        assert_eq!(
467            Tokens::new("0x0").next(),
468            Some(Ok(Token {
469                value: TokenValue::Term(Term::Value(Value::Integer(0x0))),
470                location: 0..3,
471            }))
472        );
473        assert_eq!(
474            Tokens::new("0X1").next(),
475            Some(Ok(Token {
476                value: TokenValue::Term(Term::Value(Value::Integer(0x1))),
477                location: 0..3,
478            }))
479        );
480        assert_eq!(
481            Tokens::new("0x19Af").next(),
482            Some(Ok(Token {
483                value: TokenValue::Term(Term::Value(Value::Integer(0x19AF))),
484                location: 0..6,
485            }))
486        );
487    }
488
489    #[test]
490    fn broken_hexadecimal_integer_constants() {
491        assert_eq!(
492            Tokens::new("0x").next(),
493            Some(Err(Error {
494                cause: TokenError::InvalidNumericConstant,
495                location: 0..2,
496            }))
497        );
498        assert_eq!(
499            Tokens::new(" 0xG ").next(),
500            Some(Err(Error {
501                cause: TokenError::InvalidNumericConstant,
502                location: 1..4,
503            }))
504        );
505        assert_eq!(
506            Tokens::new("0x1z2").next(),
507            Some(Err(Error {
508                cause: TokenError::InvalidNumericConstant,
509                location: 0..5,
510            }))
511        );
512    }
513
514    // TODO Float constants
515
516    #[test]
517    fn variables() {
518        assert_eq!(
519            Tokens::new("abc").next(),
520            Some(Ok(Token {
521                value: TokenValue::Term(Term::Variable {
522                    name: "abc",
523                    location: 0..3,
524                }),
525                location: 0..3,
526            }))
527        );
528        assert_eq!(
529            Tokens::new("foo_BAR").next(),
530            Some(Ok(Token {
531                value: TokenValue::Term(Term::Variable {
532                    name: "foo_BAR",
533                    location: 0..7,
534                }),
535                location: 0..7,
536            }))
537        );
538        assert_eq!(
539            Tokens::new("a1B2c").next(),
540            Some(Ok(Token {
541                value: TokenValue::Term(Term::Variable {
542                    name: "a1B2c",
543                    location: 0..5,
544                }),
545                location: 0..5,
546            }))
547        );
548        assert_eq!(
549            Tokens::new(" _var").next(),
550            Some(Ok(Token {
551                value: TokenValue::Term(Term::Variable {
552                    name: "_var",
553                    location: 1..5,
554                }),
555                location: 1..5,
556            }))
557        );
558    }
559
560    #[test]
561    fn operators() {
562        assert_eq!(
563            Tokens::new("?").next(),
564            Some(Ok(Token {
565                value: TokenValue::Operator(Operator::Question),
566                location: 0..1,
567            }))
568        );
569        assert_eq!(
570            Tokens::new(":").next(),
571            Some(Ok(Token {
572                value: TokenValue::Operator(Operator::Colon),
573                location: 0..1,
574            }))
575        );
576        assert_eq!(
577            Tokens::new("|").next(),
578            Some(Ok(Token {
579                value: TokenValue::Operator(Operator::Bar),
580                location: 0..1,
581            }))
582        );
583        assert_eq!(
584            Tokens::new("||").next(),
585            Some(Ok(Token {
586                value: TokenValue::Operator(Operator::BarBar),
587                location: 0..2,
588            }))
589        );
590        assert_eq!(
591            Tokens::new("|=").next(),
592            Some(Ok(Token {
593                value: TokenValue::Operator(Operator::BarEqual),
594                location: 0..2,
595            }))
596        );
597        assert_eq!(
598            Tokens::new("^").next(),
599            Some(Ok(Token {
600                value: TokenValue::Operator(Operator::Caret),
601                location: 0..1,
602            }))
603        );
604        assert_eq!(
605            Tokens::new("^=").next(),
606            Some(Ok(Token {
607                value: TokenValue::Operator(Operator::CaretEqual),
608                location: 0..2,
609            }))
610        );
611        assert_eq!(
612            Tokens::new("&").next(),
613            Some(Ok(Token {
614                value: TokenValue::Operator(Operator::And),
615                location: 0..1,
616            }))
617        );
618        assert_eq!(
619            Tokens::new("&&").next(),
620            Some(Ok(Token {
621                value: TokenValue::Operator(Operator::AndAnd),
622                location: 0..2,
623            }))
624        );
625        assert_eq!(
626            Tokens::new("&=").next(),
627            Some(Ok(Token {
628                value: TokenValue::Operator(Operator::AndEqual),
629                location: 0..2,
630            }))
631        );
632        assert_eq!(
633            Tokens::new("=").next(),
634            Some(Ok(Token {
635                value: TokenValue::Operator(Operator::Equal),
636                location: 0..1,
637            }))
638        );
639        assert_eq!(
640            Tokens::new("==").next(),
641            Some(Ok(Token {
642                value: TokenValue::Operator(Operator::EqualEqual),
643                location: 0..2,
644            }))
645        );
646        assert_eq!(
647            Tokens::new("!=").next(),
648            Some(Ok(Token {
649                value: TokenValue::Operator(Operator::BangEqual),
650                location: 0..2,
651            }))
652        );
653        assert_eq!(
654            Tokens::new("<").next(),
655            Some(Ok(Token {
656                value: TokenValue::Operator(Operator::Less),
657                location: 0..1,
658            }))
659        );
660        assert_eq!(
661            Tokens::new("<=").next(),
662            Some(Ok(Token {
663                value: TokenValue::Operator(Operator::LessEqual),
664                location: 0..2,
665            }))
666        );
667        assert_eq!(
668            Tokens::new("<<").next(),
669            Some(Ok(Token {
670                value: TokenValue::Operator(Operator::LessLess),
671                location: 0..2,
672            }))
673        );
674        assert_eq!(
675            Tokens::new("<<=").next(),
676            Some(Ok(Token {
677                value: TokenValue::Operator(Operator::LessLessEqual),
678                location: 0..3,
679            }))
680        );
681        assert_eq!(
682            Tokens::new(">").next(),
683            Some(Ok(Token {
684                value: TokenValue::Operator(Operator::Greater),
685                location: 0..1,
686            }))
687        );
688        assert_eq!(
689            Tokens::new(">=").next(),
690            Some(Ok(Token {
691                value: TokenValue::Operator(Operator::GreaterEqual),
692                location: 0..2,
693            }))
694        );
695        assert_eq!(
696            Tokens::new(">>").next(),
697            Some(Ok(Token {
698                value: TokenValue::Operator(Operator::GreaterGreater),
699                location: 0..2,
700            }))
701        );
702        assert_eq!(
703            Tokens::new(">>=").next(),
704            Some(Ok(Token {
705                value: TokenValue::Operator(Operator::GreaterGreaterEqual),
706                location: 0..3,
707            }))
708        );
709        assert_eq!(
710            Tokens::new("+").next(),
711            Some(Ok(Token {
712                value: TokenValue::Operator(Operator::Plus),
713                location: 0..1,
714            }))
715        );
716        assert_eq!(
717            Tokens::new("++").next(),
718            Some(Ok(Token {
719                value: TokenValue::Operator(Operator::PlusPlus),
720                location: 0..2,
721            }))
722        );
723        assert_eq!(
724            Tokens::new("+=").next(),
725            Some(Ok(Token {
726                value: TokenValue::Operator(Operator::PlusEqual),
727                location: 0..2,
728            }))
729        );
730        assert_eq!(
731            Tokens::new("-").next(),
732            Some(Ok(Token {
733                value: TokenValue::Operator(Operator::Minus),
734                location: 0..1,
735            }))
736        );
737        assert_eq!(
738            Tokens::new("--").next(),
739            Some(Ok(Token {
740                value: TokenValue::Operator(Operator::MinusMinus),
741                location: 0..2,
742            }))
743        );
744        assert_eq!(
745            Tokens::new("-=").next(),
746            Some(Ok(Token {
747                value: TokenValue::Operator(Operator::MinusEqual),
748                location: 0..2,
749            }))
750        );
751        assert_eq!(
752            Tokens::new("*").next(),
753            Some(Ok(Token {
754                value: TokenValue::Operator(Operator::Asterisk),
755                location: 0..1,
756            }))
757        );
758        assert_eq!(
759            Tokens::new("*=").next(),
760            Some(Ok(Token {
761                value: TokenValue::Operator(Operator::AsteriskEqual),
762                location: 0..2,
763            }))
764        );
765        assert_eq!(
766            Tokens::new("/").next(),
767            Some(Ok(Token {
768                value: TokenValue::Operator(Operator::Slash),
769                location: 0..1,
770            }))
771        );
772        assert_eq!(
773            Tokens::new("/=").next(),
774            Some(Ok(Token {
775                value: TokenValue::Operator(Operator::SlashEqual),
776                location: 0..2,
777            }))
778        );
779        assert_eq!(
780            Tokens::new("%").next(),
781            Some(Ok(Token {
782                value: TokenValue::Operator(Operator::Percent),
783                location: 0..1,
784            }))
785        );
786        assert_eq!(
787            Tokens::new("%=").next(),
788            Some(Ok(Token {
789                value: TokenValue::Operator(Operator::PercentEqual),
790                location: 0..2,
791            }))
792        );
793        assert_eq!(
794            Tokens::new("~").next(),
795            Some(Ok(Token {
796                value: TokenValue::Operator(Operator::Tilde),
797                location: 0..1,
798            }))
799        );
800        assert_eq!(
801            Tokens::new("!").next(),
802            Some(Ok(Token {
803                value: TokenValue::Operator(Operator::Bang),
804                location: 0..1,
805            }))
806        );
807        assert_eq!(
808            Tokens::new("(").next(),
809            Some(Ok(Token {
810                value: TokenValue::Operator(Operator::OpenParen),
811                location: 0..1
812            }))
813        );
814        assert_eq!(
815            Tokens::new("(").next(),
816            Some(Ok(Token {
817                value: TokenValue::Operator(Operator::OpenParen),
818                location: 0..1
819            }))
820        );
821    }
822
823    #[test]
824    fn space_around_token() {
825        assert_eq!(
826            Tokens::new(" 42").next(),
827            Some(Ok(Token {
828                value: TokenValue::Term(Term::Value(Value::Integer(42))),
829                location: 1..3,
830            }))
831        );
832        assert_eq!(
833            Tokens::new("042 ").next(),
834            Some(Ok(Token {
835                value: TokenValue::Term(Term::Value(Value::Integer(0o42))),
836                location: 0..3,
837            }))
838        );
839        assert_eq!(
840            Tokens::new("\t 123 \n").next(),
841            Some(Ok(Token {
842                value: TokenValue::Term(Term::Value(Value::Integer(123))),
843                location: 2..5,
844            }))
845        );
846    }
847
848    #[test]
849    fn parsing_two_tokens() {
850        let mut tokens = Tokens::new(" 123  foo ");
851        assert_eq!(
852            tokens.next(),
853            Some(Ok(Token {
854                value: TokenValue::Term(Term::Value(Value::Integer(123))),
855                location: 1..4,
856            }))
857        );
858        assert_eq!(
859            tokens.next(),
860            Some(Ok(Token {
861                value: TokenValue::Term(Term::Variable {
862                    name: "foo",
863                    location: 6..9,
864                }),
865                location: 6..9,
866            }))
867        );
868        assert_eq!(
869            tokens.next(),
870            Some(Ok(Token {
871                value: TokenValue::EndOfInput,
872                location: 10..10,
873            }))
874        );
875    }
876
877    #[test]
878    fn parsing_many_tokens() {
879        // TODO "10.0e+3+0"
880        let mut tokens = Tokens::new(" 10+0 ");
881        assert_eq!(
882            tokens.next(),
883            Some(Ok(Token {
884                value: TokenValue::Term(Term::Value(Value::Integer(10))),
885                location: 1..3,
886            }))
887        );
888        assert_eq!(
889            tokens.next(),
890            Some(Ok(Token {
891                value: TokenValue::Operator(Operator::Plus),
892                location: 3..4,
893            }))
894        );
895        assert_eq!(
896            tokens.next(),
897            Some(Ok(Token {
898                value: TokenValue::Term(Term::Value(Value::Integer(0))),
899                location: 4..5,
900            }))
901        );
902        assert_eq!(
903            tokens.next(),
904            Some(Ok(Token {
905                value: TokenValue::EndOfInput,
906                location: 6..6,
907            }))
908        );
909    }
910
911    #[test]
912    fn parsing_adjacent_operators() {
913        let mut tokens = Tokens::new("+-0");
914        assert_eq!(
915            tokens.next(),
916            Some(Ok(Token {
917                value: TokenValue::Operator(Operator::Plus),
918                location: 0..1,
919            }))
920        );
921        assert_eq!(
922            tokens.next(),
923            Some(Ok(Token {
924                value: TokenValue::Operator(Operator::Minus),
925                location: 1..2,
926            }))
927        );
928        assert_eq!(
929            tokens.next(),
930            Some(Ok(Token {
931                value: TokenValue::Term(Term::Value(Value::Integer(0))),
932                location: 2..3,
933            }))
934        );
935        assert_eq!(
936            tokens.next(),
937            Some(Ok(Token {
938                value: TokenValue::EndOfInput,
939                location: 3..3,
940            }))
941        );
942    }
943
944    #[test]
945    fn unrecognized_character() {
946        assert_eq!(
947            Tokens::new("#").next(),
948            Some(Err(Error {
949                cause: TokenError::InvalidCharacter,
950                location: 0..1,
951            }))
952        );
953        assert_eq!(
954            Tokens::new(" @@").next(),
955            Some(Err(Error {
956                cause: TokenError::InvalidCharacter,
957                location: 1..2,
958            }))
959        );
960    }
961
962    #[test]
963    fn peekable_tokens() {
964        let mut tokens = PeekableTokens::from("1 + 2");
965        assert_eq!(
966            tokens.peek(),
967            &Ok(Token {
968                value: TokenValue::Term(Term::Value(Value::Integer(1))),
969                location: 0..1,
970            })
971        );
972        assert_eq!(
973            tokens.peek(),
974            &Ok(Token {
975                value: TokenValue::Term(Term::Value(Value::Integer(1))),
976                location: 0..1,
977            })
978        );
979        assert_eq!(
980            tokens.next(),
981            Ok(Token {
982                value: TokenValue::Term(Term::Value(Value::Integer(1))),
983                location: 0..1,
984            })
985        );
986
987        assert_eq!(
988            tokens.peek(),
989            &Ok(Token {
990                value: TokenValue::Operator(Operator::Plus),
991                location: 2..3,
992            })
993        );
994        assert_eq!(
995            tokens.next(),
996            Ok(Token {
997                value: TokenValue::Operator(Operator::Plus),
998                location: 2..3,
999            })
1000        );
1001
1002        assert_eq!(
1003            tokens.next(),
1004            Ok(Token {
1005                value: TokenValue::Term(Term::Value(Value::Integer(2))),
1006                location: 4..5,
1007            })
1008        );
1009
1010        assert_eq!(
1011            tokens.peek(),
1012            &Ok(Token {
1013                value: TokenValue::EndOfInput,
1014                location: 5..5,
1015            })
1016        );
1017        assert_eq!(
1018            tokens.next(),
1019            Ok(Token {
1020                value: TokenValue::EndOfInput,
1021                location: 5..5,
1022            })
1023        );
1024    }
1025}