Почему сопоставление по результату Regex :: find жалуется на ожидание struct regex :: Match, но найденного кортежа? - PullRequest
0 голосов
/ 12 ноября 2018

Я скопировал этот код из Code Review в IntelliJ IDEA, чтобы попытаться поиграть с ним. У меня есть домашнее задание, похожее на это (мне нужно написать версию bc для Linux на Rust), поэтому я использую этот код только для справочных целей.

use std::io;
extern crate regex;
#[macro_use]
extern crate lazy_static;

use regex::Regex;

fn main() {
    let tokenizer = Tokenizer::new();

    loop {
        println!("Enter input:");
        let mut input = String::new();
        io::stdin()
            .read_line(&mut input)
            .expect("Failed to read line");
        let tokens = tokenizer.tokenize(&input);
        let stack = shunt(tokens);
        let res = calculate(stack);
        println!("{}", res);
    }
}

#[derive(Debug, PartialEq)]
enum Token {
    Number(i64),
    Plus,
    Sub,
    Mul,
    Div,
    LeftParen,
    RightParen,
}

impl Token {
    /// Returns the precedence of op
    fn precedence(&self) -> usize {
        match *self {
            Token::Plus | Token::Sub => 1,
            Token::Mul | Token::Div => 2,
            _ => 0,
        }
    }
}

struct Tokenizer {
    number: Regex,
}

impl Tokenizer {
    fn new() -> Tokenizer {
        Tokenizer {
            number: Regex::new(r"^[0-9]+").expect("Unable to create the regex"),
        }
    }

    /// Tokenizes the input string into a Vec of Tokens.
    fn tokenize(&self, mut input: &str) -> Vec<Token> {
        let mut res = vec![];

        loop {
            input = input.trim_left();
            if input.is_empty() { break }

            let (token, rest) = match self.number.find(input) {
                Some((_, end)) => {
                    let (num, rest) = input.split_at(end);
                    (Token::Number(num.parse().unwrap()), rest)
                },
                _ => {
                    match input.chars().next() {
                        Some(chr) => {
                            (match chr {
                                '+' => Token::Plus,
                                '-' => Token::Sub,
                                '*' => Token::Mul,
                                '/' => Token::Div,
                                '(' => Token::LeftParen,
                                ')' => Token::RightParen,
                                _ => panic!("Unknown character!"),
                            }, &input[chr.len_utf8()..])
                        }
                        None => panic!("Ran out of input"),
                    }
                }
            };

            res.push(token);
            input = rest;
        }

        res
    }
}

/// Transforms the tokens created by `tokenize` into RPN using the
/// [Shunting-yard algorithm](https://en.wikipedia.org/wiki/Shunting-yard_algorithm)
fn shunt(tokens: Vec<Token>) -> Vec<Token> {
    let mut queue = vec![];
    let mut stack: Vec<Token> = vec![];
    for token in tokens {
        match token {
            Token::Number(_) => queue.push(token),
            Token::Plus | Token::Sub | Token::Mul | Token::Div => {
                while let Some(o) = stack.pop() {
                    if token.precedence() <= o.precedence() {
                        queue.push(o);
                    } else {
                        stack.push(o);
                        break;
                    }
                }
                stack.push(token)
            },
            Token::LeftParen => stack.push(token),
            Token::RightParen => {
                let mut found_paren = false;
                while let Some(op) = stack.pop() {
                    match op {
                        Token::LeftParen => {
                            found_paren = true;
                            break;
                        },
                        _ => queue.push(op),
                    }
                }
                assert!(found_paren)
            },
        }
    }
    while let Some(op) = stack.pop() {
        queue.push(op);
    }
    queue
}

/// Takes a Vec of Tokens converted to RPN by `shunt` and calculates the result
fn calculate(tokens: Vec<Token>) -> i64 {
    let mut stack = vec![];
    for token in tokens {
        match token {
            Token::Number(n) => stack.push(n),
            Token::Plus => {
                let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap());
                stack.push(a + b);
            },
            Token::Sub => {
                let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap());
                stack.push(a - b);
            },
            Token::Mul => {
                let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap());
                stack.push(a * b);
            },
            Token::Div => {
                let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap());
                stack.push(a / b);
            },
            _ => {
                // By the time the token stream gets here, all the LeftParen
                // and RightParen tokens will have been removed by shunt()
                unreachable!();
            },
        }
    }
    stack[0]
}

Когда я запускаю его, он выдает мне эту ошибку:

error[E0308]: mismatched types
  --> src\main.rs:66:22
   |
66 |                 Some((_, end)) => {
   |                      ^^^^^^^^ expected struct `regex::Match`, found tuple
   |
   = note: expected type `regex::Match<'_>`
              found type `(_, _)`

Жалуется, что я использую кортеж для метода Some(), когда я должен использовать токен. Я не уверен, что передать для токена, потому что кажется, что кортеж перебирает опции Token. Как мне переписать это, чтобы метод Some() распознал кортеж как Token? Я работал над этим в течение дня, но я не нашел действительно хороших решений.

1 Ответ

0 голосов
/ 12 ноября 2018

Код, на который вы ссылаетесь, старше двух лет. Примечательно, что это предшествовало регулярному выражению 1.0. Версия 0.1.80 определяет Regex::find как:

fn find(&self, text: &str) -> Option<(usize, usize)>

, а версия 1.0.6 определяет его как :

pub fn find<'t>(&self, text: &'t str) -> Option<Match<'t>>

Тем не менее, Match определяет методы для получения начального и конечного индексов, предполагаемых при написании кода. В этом случае, поскольку вы заботитесь только о конечном индексе, вы можете позвонить Match::end:

let (token, rest) = match self.number.find(input).map(|x| x.end()) {
    Some(end) => {
    // ...
...