rink_core/parsing/
text_query.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5use crate::ast::*;
6use crate::output::Digits;
7use crate::types::{BigInt, BigRat, Numeric};
8use chrono_tz::Tz;
9use std::iter::Peekable;
10use std::str::Chars;
11
12#[derive(Debug, Clone)]
13pub enum Token {
14    Newline,
15    Comment(usize),
16    Ident(String),
17    Decimal(String, Option<String>, Option<String>),
18    Hex(String),
19    Oct(String),
20    Bin(String),
21    Quote(String),
22    Slash,
23    Pipe,
24    Semicolon,
25    Equals,
26    Caret,
27    Eof,
28    LPar,
29    RPar,
30    Plus,
31    Minus,
32    Asterisk,
33    DashArrow,
34    Colon,
35    DoubleLAngle,
36    DoubleRAngle,
37    KeywordMod,
38    KeywordXor,
39    KeywordOr,
40    KeywordAnd,
41    Date(Vec<DateToken>),
42    Comma,
43    Degree(Degree),
44    Percent,
45    Error(String),
46}
47
48fn describe(token: &Token) -> String {
49    match *token {
50        Token::Newline | Token::Comment(_) => "\\n".to_owned(),
51        Token::Ident(_) => "ident".to_owned(),
52        Token::Decimal(_, _, _) => "number".to_owned(),
53        Token::Hex(_) => "hex".to_owned(),
54        Token::Oct(_) => "octal".to_owned(),
55        Token::Bin(_) => "binary".to_owned(),
56        Token::Quote(_) => "quote".to_owned(),
57        Token::Slash => "`/`".to_owned(),
58        Token::Pipe => "`|`".to_owned(),
59        Token::Semicolon => "`;`".to_owned(),
60        Token::Equals => "`=`".to_owned(),
61        Token::Caret => "`^`".to_owned(),
62        Token::Eof => "eof".to_owned(),
63        Token::LPar => "`(`".to_owned(),
64        Token::RPar => "`)`".to_owned(),
65        Token::Plus => "`+`".to_owned(),
66        Token::Minus => "`-`".to_owned(),
67        Token::Asterisk => "`*`".to_owned(),
68        Token::DashArrow => "`->`".to_owned(),
69        Token::Colon => "`:`".to_owned(),
70        Token::DoubleLAngle => "`<<`".to_owned(),
71        Token::DoubleRAngle => "`>>`".to_owned(),
72        Token::KeywordMod => "`mod`".to_owned(),
73        Token::KeywordXor => "`xor`".to_owned(),
74        Token::KeywordOr => "`or`".to_owned(),
75        Token::KeywordAnd => "`and`".to_owned(),
76        Token::Date(_) => "date literal".to_owned(),
77        Token::Comma => "`,`".to_owned(),
78        Token::Percent => "%".to_owned(),
79        Token::Degree(ref deg) => format!("`{}`", deg),
80        Token::Error(ref e) => format!("<{}>", e),
81    }
82}
83
84#[derive(Clone)]
85pub struct TokenIterator<'a>(Peekable<Chars<'a>>);
86
87impl<'a> TokenIterator<'a> {
88    pub fn new(input: &'a str) -> TokenIterator<'a> {
89        TokenIterator(input.chars().peekable())
90    }
91}
92
93impl<'a> Iterator for TokenIterator<'a> {
94    type Item = Token;
95
96    fn next(&mut self) -> Option<Token> {
97        if self.0.peek().is_none() {
98            return Some(Token::Eof);
99        }
100        let res = match self.0.next().unwrap() {
101            ' ' | '\t' => return self.next(),
102            '\n' => Token::Newline,
103            '(' => Token::LPar,
104            ')' => Token::RPar,
105            '+' => Token::Plus,
106            ';' => Token::Semicolon,
107            '%' => Token::Percent,
108            '=' => Token::Equals,
109            '^' => Token::Caret,
110            ',' => Token::Comma,
111            // U+2215 ∕ DIVISION SLASH
112            // Used by rink-web to render these tight fractions.
113            '|' | '\u{2215}' => Token::Pipe,
114            ':' => Token::Colon,
115            '→' => Token::DashArrow,
116            '<' if self.0.peek().cloned() == Some('<') => {
117                self.0.next();
118                Token::DoubleLAngle
119            }
120            '>' if self.0.peek().cloned() == Some('>') => {
121                self.0.next();
122                Token::DoubleRAngle
123            }
124            '*' => {
125                if self.0.peek().cloned() == Some('*') {
126                    self.0.next();
127                    Token::Caret
128                } else {
129                    Token::Asterisk
130                }
131            }
132            '-' => match self.0.peek().cloned() {
133                Some('>') => {
134                    self.0.next();
135                    Token::DashArrow
136                }
137                _ => Token::Minus,
138            },
139            '\u{2212}' => Token::Minus,
140            '/' => match self.0.peek() {
141                Some(&'/') => loop {
142                    match self.0.next() {
143                        None | Some('\n') => return Some(Token::Comment(1)),
144                        _ => (),
145                    }
146                },
147                Some(&'*') => {
148                    let mut lines = 0;
149                    loop {
150                        if let Some(&'\n') = self.0.peek() {
151                            lines += 1;
152                        }
153                        if let Some('*') = self.0.next() {
154                            if let Some(&'/') = self.0.peek() {
155                                self.0.next();
156                                return Some(Token::Comment(lines));
157                            }
158                        }
159                        if self.0.peek() == None {
160                            return Some(Token::Error("Expected `*/`, got EOF".to_string()));
161                        }
162                    }
163                }
164                _ => Token::Slash,
165            },
166            x @ '0'..='9' | x @ '.' => {
167                if x == '0' && self.0.peek() == Some(&'x') {
168                    self.0.next();
169                    let mut hex = String::new();
170
171                    while let Some(c) = self.0.peek().cloned() {
172                        match c {
173                            '0'..='9' | 'a'..='f' | 'A'..='F' => hex.push(self.0.next().unwrap()),
174                            '\u{2009}' | '_' => {
175                                self.0.next();
176                            }
177                            _ => break,
178                        }
179                    }
180                    if hex.is_empty() {
181                        return Some(Token::Error(
182                            "Malformed hexadecimal literal: No digits after 0x".to_owned(),
183                        ));
184                    }
185                    return Some(Token::Hex(hex));
186                }
187
188                if x == '0' && self.0.peek() == Some(&'o') {
189                    self.0.next();
190                    let mut oct = String::new();
191
192                    while let Some(c) = self.0.peek().cloned() {
193                        match c {
194                            '0'..='7' => oct.push(self.0.next().unwrap()),
195                            '\u{2009}' | '_' => {
196                                self.0.next();
197                            }
198                            _ => break,
199                        }
200                    }
201                    if oct.is_empty() {
202                        return Some(Token::Error(
203                            "Malformed octal literal: No digits after 0o".to_owned(),
204                        ));
205                    }
206                    return Some(Token::Oct(oct));
207                }
208
209                if x == '0' && self.0.peek() == Some(&'b') {
210                    self.0.next();
211                    let mut bin = String::new();
212
213                    while let Some(c) = self.0.peek().cloned() {
214                        match c {
215                            '0' | '1' => bin.push(self.0.next().unwrap()),
216                            '\u{2009}' | '_' => {
217                                self.0.next();
218                            }
219                            _ => break,
220                        }
221                    }
222                    if bin.is_empty() {
223                        return Some(Token::Error(
224                            "Malformed binary literal: No digits after 0b".to_owned(),
225                        ));
226                    }
227                    return Some(Token::Bin(bin));
228                }
229
230                let mut integer = String::new();
231                let mut frac = None;
232                let mut exp = None;
233
234                // integer component
235                if x != '.' {
236                    integer.push(x);
237                    while let Some(c) = self.0.peek().cloned() {
238                        match c {
239                            '0'..='9' => integer.push(self.0.next().unwrap()),
240                            '\u{2009}' | '_' => {
241                                self.0.next();
242                            }
243                            _ => break,
244                        }
245                    }
246                } else {
247                    integer.push('0');
248                }
249                // fractional component
250                if x == '.' || Some('.') == self.0.peek().cloned() {
251                    let mut buf = String::new();
252                    if x != '.' {
253                        self.0.next();
254                    }
255                    while let Some(c) = self.0.peek().cloned() {
256                        match c {
257                            '0'..='9' => buf.push(self.0.next().unwrap()),
258                            '\u{2009}' | '_' => {
259                                self.0.next();
260                            }
261                            _ => break,
262                        }
263                    }
264                    if buf.is_empty() {
265                        return Some(Token::Error(
266                            "Malformed number literal: No digits after decimal point".to_owned(),
267                        ));
268                    }
269                    frac = Some(buf)
270                }
271                // exponent
272                if let Some('e') = self.0.peek().cloned().map(|x| x.to_ascii_lowercase()) {
273                    let mut buf = String::new();
274                    self.0.next();
275                    if let Some('e') = self.0.peek().cloned().map(|x| x.to_ascii_lowercase()) {
276                        self.0.next();
277                    }
278                    if let Some(c) = self.0.peek().cloned() {
279                        match c {
280                            '-' => {
281                                buf.push(self.0.next().unwrap());
282                            }
283                            '+' => {
284                                self.0.next();
285                            }
286                            _ => (),
287                        }
288                    }
289                    while let Some(c) = self.0.peek().cloned() {
290                        match c {
291                            '0'..='9' => buf.push(self.0.next().unwrap()),
292                            '\u{2009}' | '_' => {
293                                self.0.next();
294                            }
295                            _ => break,
296                        }
297                    }
298                    if buf.is_empty() {
299                        return Some(Token::Error(
300                            "Malformed number literal: No digits after exponent".to_owned(),
301                        ));
302                    }
303                    exp = Some(buf)
304                }
305                Token::Decimal(integer, frac, exp)
306            }
307            '\\' => match self.0.next() {
308                Some('u') => {
309                    let mut buf = String::new();
310                    while let Some(c) = self.0.peek().cloned() {
311                        if c.is_digit(16) {
312                            buf.push(self.0.next().unwrap());
313                        } else {
314                            break;
315                        }
316                    }
317                    let v = u32::from_str_radix(&*buf, 16).unwrap();
318                    if let Some(c) = ::std::char::from_u32(v) {
319                        let mut buf = String::new();
320                        buf.push(c);
321                        Token::Ident(buf)
322                    } else {
323                        Token::Error(format!("Invalid unicode scalar: {:x}", v))
324                    }
325                }
326                _ => Token::Error("Unexpected \\".to_string()),
327            },
328            '\'' => {
329                let mut buf = String::new();
330                loop {
331                    match self.0.next() {
332                        None | Some('\n') => {
333                            return Some(Token::Error("Unexpected newline or EOF".to_string()))
334                        }
335                        Some('\\') => match self.0.next() {
336                            Some('\'') => buf.push('\''),
337                            Some('n') => buf.push('\n'),
338                            Some('t') => buf.push('\t'),
339                            Some(c) => {
340                                return Some(Token::Error(format!(
341                                    "Invalid escape sequence \\{}",
342                                    c
343                                )))
344                            }
345                            None => return Some(Token::Error("Unexpected EOF".to_string())),
346                        },
347                        Some('\'') => break,
348                        Some(c) => buf.push(c),
349                    }
350                }
351                Token::Quote(buf)
352            }
353            '#' => {
354                let mut toks = vec![];
355                while self.0.peek().is_some() {
356                    let res = match self.0.next().unwrap() {
357                        '#' => break,
358                        ':' => DateToken::Colon,
359                        '-' => DateToken::Dash,
360                        '+' => DateToken::Plus,
361                        x if x.is_whitespace() => {
362                            while self.0.peek().map(|c| c.is_whitespace()).unwrap_or(false) {
363                                self.0.next();
364                            }
365                            DateToken::Space
366                        }
367                        x if x.is_digit(10) => {
368                            let mut integer = String::new();
369                            integer.push(x);
370                            while let Some(c) = self.0.peek().cloned() {
371                                if c.is_digit(10) {
372                                    self.0.next();
373                                    integer.push(c);
374                                } else {
375                                    break;
376                                }
377                            }
378                            let frac = if let Some('.') = self.0.peek().cloned() {
379                                let mut frac = String::new();
380                                self.0.next();
381                                while let Some(c) = self.0.peek().cloned() {
382                                    if c.is_digit(10) {
383                                        self.0.next();
384                                        frac.push(c);
385                                    } else {
386                                        break;
387                                    }
388                                }
389                                Some(frac)
390                            } else {
391                                None
392                            };
393                            DateToken::Number(integer, frac)
394                        }
395                        x => {
396                            let mut buf = String::new();
397                            buf.push(x);
398                            while let Some(c) = self.0.peek().cloned() {
399                                if !"#:-+ ".contains(c) && !c.is_digit(10) {
400                                    self.0.next();
401                                    buf.push(c);
402                                } else {
403                                    break;
404                                }
405                            }
406                            DateToken::Literal(buf)
407                        } //x => DateToken::Error(format!("Unexpected character '{}'", x))
408                    };
409                    toks.push(res);
410                }
411                if let Some(&DateToken::Space) = toks.first() {
412                    toks.remove(0);
413                }
414                if let Some(&DateToken::Space) = toks.last() {
415                    toks.pop();
416                }
417                Token::Date(toks)
418            }
419            '"' => {
420                let mut buf = String::new();
421                while let Some(c) = self.0.next() {
422                    if c == '\\' {
423                        if let Some(c) = self.0.next() {
424                            buf.push(c);
425                        }
426                    } else if c == '"' {
427                        break;
428                    } else {
429                        buf.push(c);
430                    }
431                }
432                Token::Ident(buf)
433            }
434            x => {
435                let mut buf = String::new();
436                buf.push(x);
437                while let Some(c) = self.0.peek().cloned() {
438                    if c.is_alphanumeric() || c == '_' || c == '$' {
439                        buf.push(self.0.next().unwrap());
440                    } else {
441                        break;
442                    }
443                }
444                match &*buf {
445                    "degC" | "°C" | "celsius" | "℃" => Token::Degree(Degree::Celsius),
446                    "degF" | "°F" | "fahrenheit" | "℉" => Token::Degree(Degree::Fahrenheit),
447                    "degRé" | "°Ré" | "degRe" | "°Re" | "réaumur" | "reaumur" => {
448                        Token::Degree(Degree::Reaumur)
449                    }
450                    "degRø" | "°Rø" | "degRo" | "°Ro" | "rømer" | "romer" => {
451                        Token::Degree(Degree::Romer)
452                    }
453                    "degDe" | "°De" | "delisle" => Token::Degree(Degree::Delisle),
454                    "degN" | "°N" | "degnewton" => Token::Degree(Degree::Newton),
455                    "per" => Token::Slash,
456                    "to" | "in" => Token::DashArrow,
457                    "mod" => Token::KeywordMod,
458                    "and" => Token::KeywordAnd,
459                    "or" => Token::KeywordOr,
460                    "xor" => Token::KeywordXor,
461                    _ => Token::Ident(buf),
462                }
463            }
464        };
465        Some(res)
466    }
467}
468
469pub type Iter<'a> = Peekable<TokenIterator<'a>>;
470
471fn attr_from_name(name: &str) -> Option<&'static str> {
472    match name {
473        "int" | "international" => Some("int"),
474        "UKSJJ" => Some("UKSJJ"),
475        "UKB" => Some("UKB"),
476        "UKC" => Some("UKC"),
477        "UKK" => Some("UKK"),
478        "imperial" | "british" | "UK" => Some("br"),
479        "survey" | "geodetic" => Some("survey"),
480        "irish" => Some("irish"),
481        "aust" | "australian" => Some("aust"),
482        "roman" => Some("roman"),
483        "egyptian" => Some("egyptian"),
484        "greek" => Some("greek"),
485        "olympic" => Some("olympic"),
486        _ => None,
487    }
488}
489
490fn parse_function(iter: &mut Iter<'_>, func: Function) -> Expr {
491    let args = match iter.peek().cloned().unwrap() {
492        Token::LPar => {
493            iter.next();
494            let mut args = vec![];
495            loop {
496                if let Some(&Token::RPar) = iter.peek() {
497                    iter.next();
498                    break;
499                }
500                args.push(parse_expr(iter));
501                match iter.peek().cloned().unwrap() {
502                    Token::Comma => {
503                        iter.next();
504                    }
505                    Token::RPar => (),
506                    x => {
507                        return Expr::new_error(format!(
508                            "Expected `,` or `)`, got {}",
509                            describe(&x)
510                        ))
511                    }
512                }
513            }
514            args
515        }
516        _ => vec![parse_pow(iter)],
517    };
518    Expr::new_call(func, args)
519}
520
521fn parse_radix(num: &str, base: u32, description: &str) -> Expr {
522    BigInt::from_str_radix(num, base)
523        .map(|x| BigRat::ratio(&x, &BigInt::one()))
524        .map(Numeric::Rational)
525        .map(Expr::new_const)
526        .unwrap_or_else(|_| Expr::new_error(format!("Failed to parse {}", description)))
527}
528
529fn parse_term(iter: &mut Iter<'_>) -> Expr {
530    match iter.next().unwrap() {
531        Token::Ident(ref id) => {
532            if let Some(func) = Function::from_name(id) {
533                parse_function(iter, func)
534            } else if let Some(attr) = attr_from_name(id) {
535                match iter.peek().cloned().unwrap() {
536                    Token::Ident(ref name) => {
537                        iter.next();
538                        Expr::new_unit(format!("{}{}", attr, name))
539                    }
540                    x => Expr::new_error(format!(
541                        "Attribute must be followed by ident, got {}",
542                        describe(&x)
543                    )),
544                }
545            } else {
546                match iter.peek().cloned().unwrap() {
547                    Token::Ident(ref s) if s == "of" => {
548                        iter.next();
549                        Expr::new_of(id, parse_juxt(iter))
550                    }
551                    _ => Expr::new_unit(id.to_string()),
552                }
553            }
554        }
555        Token::Quote(string) => Expr::Quote { string },
556        Token::Decimal(num, frac, exp) => crate::types::Number::from_parts(
557            &*num,
558            frac.as_ref().map(|x| &**x),
559            exp.as_ref().map(|x| &**x),
560        )
561        .map(Expr::new_const)
562        .unwrap_or_else(Expr::new_error),
563        Token::Hex(num) => parse_radix(&*num, 16, "hex"),
564        Token::Oct(num) => parse_radix(&*num, 8, "octal"),
565        Token::Bin(num) => parse_radix(&*num, 2, "binary"),
566        Token::Plus => Expr::new_plus(parse_term(iter)),
567        Token::Minus => Expr::new_negate(parse_term(iter)),
568        Token::LPar => {
569            let res = parse_expr(iter);
570            match iter.next().unwrap() {
571                Token::RPar => res,
572                x => Expr::new_error(format!("Expected `)`, got {}", describe(&x))),
573            }
574        }
575        Token::Percent => Expr::new_unit("percent".to_owned()),
576        Token::Date(tokens) => Expr::Date { tokens },
577        Token::Comment(_) => parse_term(iter),
578        x => Expr::new_error(format!("Expected term, got {}", describe(&x))),
579    }
580}
581
582fn parse_suffix(iter: &mut Iter<'_>) -> Expr {
583    let left = parse_term(iter);
584    match *iter.peek().unwrap() {
585        Token::Percent => {
586            let mut left = left;
587            while let Some(&Token::Percent) = iter.peek() {
588                iter.next();
589                left = Expr::new_mul(vec![left, Expr::new_unit("percent".to_owned())]);
590            }
591            left
592        }
593        _ => left,
594    }
595}
596
597fn parse_pow(iter: &mut Iter<'_>) -> Expr {
598    let left = parse_suffix(iter);
599    match *iter.peek().unwrap() {
600        Token::Caret => {
601            iter.next();
602            let right = parse_pow(iter);
603            Expr::new_pow(left, right)
604        }
605        _ => left,
606    }
607}
608
609fn parse_frac(iter: &mut Iter<'_>) -> Expr {
610    let left = parse_pow(iter);
611    match *iter.peek().unwrap() {
612        Token::Pipe => {
613            iter.next();
614            let right = parse_pow(iter);
615            Expr::new_frac(left, right)
616        }
617        _ => left,
618    }
619}
620
621fn parse_juxt(iter: &mut Iter<'_>) -> Expr {
622    let mut terms = vec![parse_frac(iter)];
623    loop {
624        match iter.peek().cloned().unwrap() {
625            Token::Asterisk
626            | Token::Slash
627            | Token::Comma
628            | Token::Equals
629            | Token::Plus
630            | Token::Minus
631            | Token::DashArrow
632            | Token::RPar
633            | Token::Newline
634            | Token::DoubleLAngle
635            | Token::DoubleRAngle
636            | Token::KeywordMod
637            | Token::KeywordAnd
638            | Token::KeywordOr
639            | Token::KeywordXor
640            | Token::Comment(_)
641            | Token::Eof => break,
642            Token::Degree(deg) => {
643                iter.next();
644                terms = vec![Expr::new_suffix(deg, Expr::new_mul(terms))]
645            }
646            _ => terms.push(parse_frac(iter)),
647        }
648    }
649    if terms.len() == 1 {
650        terms.pop().unwrap()
651    } else {
652        Expr::new_mul(terms)
653    }
654}
655
656fn parse_div(iter: &mut Iter<'_>) -> Expr {
657    let mut terms = vec![parse_juxt(iter)];
658    loop {
659        match iter.peek().cloned().unwrap() {
660            Token::Slash => {
661                iter.next();
662                let left = Expr::new_mul(terms.drain(..).collect());
663                terms = vec![Expr::new_frac(left, parse_juxt(iter))];
664            }
665            Token::Asterisk => {
666                iter.next();
667                terms.push(parse_juxt(iter));
668            }
669            Token::DoubleLAngle => {
670                iter.next();
671                let left = Expr::new_mul(terms.drain(..).collect());
672                terms = vec![Expr::new_bin(BinOpType::ShiftL, left, parse_juxt(iter))];
673            }
674            Token::DoubleRAngle => {
675                iter.next();
676                let left = Expr::new_mul(terms.drain(..).collect());
677                terms = vec![Expr::new_bin(BinOpType::ShiftR, left, parse_juxt(iter))];
678            }
679            Token::KeywordMod => {
680                iter.next();
681                let left = Expr::new_mul(terms.drain(..).collect());
682                terms = vec![Expr::new_bin(BinOpType::Mod, left, parse_juxt(iter))];
683            }
684            Token::KeywordAnd => {
685                iter.next();
686                let left = Expr::new_mul(terms.drain(..).collect());
687                terms = vec![Expr::new_bin(BinOpType::And, left, parse_juxt(iter))];
688            }
689            Token::KeywordOr => {
690                iter.next();
691                let left = Expr::new_mul(terms.drain(..).collect());
692                terms = vec![Expr::new_bin(BinOpType::Or, left, parse_juxt(iter))];
693            }
694            Token::KeywordXor => {
695                iter.next();
696                let left = Expr::new_mul(terms.drain(..).collect());
697                terms = vec![Expr::new_bin(BinOpType::Xor, left, parse_juxt(iter))];
698            }
699            _ => break,
700        }
701    }
702    if terms.len() == 1 {
703        terms.pop().unwrap()
704    } else {
705        Expr::new_mul(terms)
706    }
707}
708
709fn parse_add(iter: &mut Iter<'_>) -> Expr {
710    let mut left = parse_div(iter);
711    loop {
712        match *iter.peek().unwrap() {
713            Token::Plus => {
714                iter.next();
715                let right = parse_div(iter);
716                left = Expr::new_add(left, right)
717            }
718            Token::Minus => {
719                iter.next();
720                let right = parse_div(iter);
721                left = Expr::new_sub(left, right)
722            }
723            _ => return left,
724        }
725    }
726}
727
728fn parse_eq(iter: &mut Iter<'_>) -> Expr {
729    let left = parse_add(iter);
730    match iter.peek().cloned().unwrap() {
731        Token::Equals => {
732            iter.next();
733            let right = parse_add(iter);
734            Expr::new_equals(left, right)
735        }
736        _ => left,
737    }
738}
739
740pub fn parse_expr(iter: &mut Iter<'_>) -> Expr {
741    parse_eq(iter)
742}
743
744pub fn parse_unitlist(iter: &mut Iter<'_>) -> Option<Vec<String>> {
745    let mut expecting_term = true;
746    let mut res = vec![];
747    loop {
748        match iter.next().unwrap() {
749            Token::Ident(ref ident) if expecting_term => {
750                res.push(ident.clone());
751                expecting_term = false;
752            }
753            Token::Comma | Token::Semicolon if !expecting_term => {
754                expecting_term = true;
755            }
756            Token::Eof | Token::Newline | Token::Comment(_) if !expecting_term => break,
757            _ => return None,
758        }
759    }
760    if res.len() > 1 {
761        Some(res)
762    } else {
763        None
764    }
765}
766
767pub fn parse_offset(iter: &mut Iter<'_>) -> Option<i64> {
768    use std::str::FromStr;
769
770    let sign = match iter.next().unwrap() {
771        Token::Plus => 1,
772        Token::Minus => -1,
773        _ => return None,
774    };
775    let hour = match iter.next().unwrap() {
776        Token::Decimal(ref i, None, None) if i.len() == 2 => i.clone(),
777        _ => return None,
778    };
779    match iter.next().unwrap() {
780        Token::Colon => (),
781        _ => return None,
782    }
783    let min = match iter.next().unwrap() {
784        Token::Decimal(ref i, None, None) if i.len() == 2 => i.clone(),
785        _ => return None,
786    };
787    Some(sign * (i64::from_str(&*hour).unwrap() * 3600 + i64::from_str(&*min).unwrap() * 60))
788}
789
790pub fn parse_query(iter: &mut Iter<'_>) -> Query {
791    match iter.peek().cloned() {
792        Some(Token::Ident(ref s)) if s == "factorize" => {
793            iter.next();
794            return Query::Factorize(parse_eq(iter));
795        }
796        Some(Token::Ident(ref s)) if s == "units" => {
797            iter.next();
798            if let Some(Token::Ident(ref s)) = iter.peek().cloned() {
799                if s == "for" || s == "of" {
800                    iter.next();
801                }
802            }
803            return Query::UnitsFor(parse_eq(iter));
804        }
805        Some(Token::Ident(ref s)) if s == "search" => {
806            iter.next();
807            if let Some(Token::Ident(ref s)) = iter.peek().cloned() {
808                return Query::Search(s.clone());
809            }
810        }
811        _ => (),
812    }
813    let left = parse_eq(iter);
814    match iter.peek().cloned().unwrap() {
815        Token::DashArrow => {
816            use std::str::FromStr;
817            iter.next();
818            let mut copy = iter.clone();
819            if let Some(res) = parse_unitlist(&mut copy) {
820                *iter = copy;
821                return Query::Convert(left, Conversion::List(res), None, Digits::Default);
822            }
823            let digits = match iter.peek().cloned().unwrap() {
824                Token::Ident(ref s) if s == "digits" => {
825                    iter.next();
826                    match iter.peek().cloned() {
827                        Some(Token::Decimal(int, None, None)) => {
828                            iter.next();
829                            match u64::from_str_radix(&*int, 10) {
830                                Ok(v) => Digits::Digits(v),
831                                Err(e) => {
832                                    return Query::Error(format!("Failed to parse digits: {}", e))
833                                }
834                            }
835                        }
836                        _ => Digits::FullInt,
837                    }
838                }
839                _ => Digits::Default,
840            };
841            let base = match iter.peek().cloned().unwrap() {
842                Token::Ident(ref s) if s == "base" => {
843                    iter.next();
844                    match iter.next() {
845                        Some(Token::Decimal(int, None, None)) => {
846                            match u64::from_str_radix(&*int, 10) {
847                                Ok(v @ 2..=36) => Some(v as u8),
848                                Ok(v) => {
849                                    return Query::Error(format!(
850                                        "Unsupported base {}, must be from 2 to 36",
851                                        v
852                                    ))
853                                }
854                                Err(e) => {
855                                    return Query::Error(format!("Failed to parse base: {}", e))
856                                }
857                            }
858                        }
859                        Some(x) => {
860                            return Query::Error(format!(
861                                "Expected decimal base, got {}",
862                                describe(&x)
863                            ))
864                        }
865                        None => return Query::Error("Expected decimal base, got eof".to_string()),
866                    }
867                }
868                Token::Ident(ref s) if s == "hex" || s == "hexadecimal" || s == "base16" => {
869                    iter.next();
870                    Some(16)
871                }
872                Token::Ident(ref s) if s == "oct" || s == "octal" || s == "base8" => {
873                    iter.next();
874                    Some(8)
875                }
876                Token::Ident(ref s) if s == "bin" || s == "binary" || s == "base2" => {
877                    iter.next();
878                    Some(2)
879                }
880                _ => None,
881            };
882            let right = match iter.peek().cloned().unwrap() {
883                Token::Eof => Conversion::None,
884                Token::Degree(deg) => Conversion::Degree(deg),
885                Token::Plus | Token::Minus => {
886                    let mut old = iter.clone();
887                    if let Some(off) = parse_offset(iter) {
888                        Conversion::Offset(off)
889                    } else {
890                        Conversion::Expr(parse_eq(&mut old))
891                    }
892                }
893                Token::Ident(ref s) if is_valid_timezone(s) => Conversion::Timezone(
894                    Tz::from_str(s).expect("Running from_str a second time failed"),
895                ),
896                _ => Conversion::Expr(parse_eq(iter)),
897            };
898            Query::Convert(left, right, base, digits)
899        }
900        _ => Query::Expr(left),
901    }
902}
903
904fn is_valid_timezone(s: &String) -> bool {
905    use std::str::FromStr;
906    s != "GB" && Tz::from_str(s).is_ok()
907}
908
909#[cfg(test)]
910mod test {
911    use super::*;
912
913    fn parse(input: &str) -> String {
914        parse_expr(&mut TokenIterator::new(input).peekable()).to_string()
915    }
916
917    #[test]
918    fn add_assoc() {
919        assert_eq!(parse("a + b - c + d - e"), "(((a + b) - c) + d) - e");
920    }
921
922    #[test]
923    fn sub_crash_regression() {
924        assert_eq!(parse("-"), "-<error: Expected term, got eof>");
925    }
926
927    #[test]
928    fn mul_assoc() {
929        assert_eq!(
930            parse("a b * c / d / e f * g h"),
931            "(((a b) c / d) / e f) (g h)"
932        );
933        assert_eq!(parse("a|b c / g e|f"), "(a / b) c / g (e / f)");
934        assert_eq!(parse("a / b / c"), "(a / b) / c");
935    }
936
937    #[test]
938    fn parse_extra_ops() {
939        assert_eq!(parse("a b mod c d"), "a b mod c d");
940        assert_eq!(parse("a b << c d"), "a b << c d");
941        assert_eq!(parse("a b >> c d"), "a b >> c d");
942        assert_eq!(parse("a b and c d"), "a b and c d");
943        assert_eq!(parse("a b or c d"), "a b or c d");
944        assert_eq!(parse("a b xor c d"), "a b xor c d");
945        assert_eq!(parse("a / b c mod d e / f"), "((a / b c) mod d e) / f");
946    }
947
948    #[test]
949    fn suffix_prec() {
950        assert_eq!(parse("a b °C + x y °F"), "a b °C + x y °F");
951        assert_eq!(parse("a b °C c"), "(a b °C) c");
952        assert_eq!(parse("a °C / x"), "a °C / x");
953        assert_eq!(parse("a °C * x"), "(a °C) x");
954    }
955
956    #[test]
957    fn number_lex() {
958        assert_eq!(
959            parse("1e"),
960            "<error: Expected term, got <Malformed number literal: No digits after exponent>>"
961        );
962        assert_eq!(
963            parse("1."),
964            "<error: Expected term, got <Malformed number literal: No digits after decimal point>>"
965        );
966    }
967
968    #[test]
969    fn mono_unit_list() {
970        use crate::ast::*;
971        match parse_query(&mut TokenIterator::new("foo -> bar").peekable()) {
972            Query::Convert(_, Conversion::Expr(_), _, _) => (),
973            x => panic!("Expected Convert(_, Expr(_), _), got {:?}", x),
974        }
975    }
976
977    #[test]
978    fn test_of() {
979        assert_eq!(parse("foo of 1 abc def / 12"), "(foo of 1 abc def) / 12");
980    }
981}