ExercĂcio: Reescrevendo com Result
O seguinte implementa um analisador muito simples para uma linguagem de expressĂŁo. No entanto, ele lida com erros disparando um pĂąnico. Reescreva-o para usar o tratamento de erros idiomĂĄtico e propagar erros para um retorno de main
. Sinta-se Ă vontade para usar thiserror
e anyhow
.
DICA: comece corrigindo o tratamento de erros na função parse
. Depois que isso estiver funcionando corretamente, atualize Tokenizer
para implementar Iterator<Item=Result<Token, TokenizerError>>
e trate isso no analisador (parser).
use std::iter::Peekable; use std::str::Chars; /// Um operador aritmĂ©tico. #[derive(Debug, PartialEq, Clone, Copy)] enum Op { Add, Sub, } /// Um token na linguagem de expressĂŁo. #[derive(Debug, PartialEq)] enum Token { Number(String), Identifier(String), Operator(Op), } /// Uma expressĂŁo na linguagem de expressĂŁo. #[derive(Debug, PartialEq)] enum Expression { /// Uma referĂȘncia a uma variĂĄvel. Var(String), /// Um nĂșmero literal. Number(u32), /// Uma operação binĂĄria. Operation(Box<Expression>, Op, Box<Expression>), } fn tokenize(input: &str) -> Tokenizer { return Tokenizer(input.chars().peekable()); } struct Tokenizer<'a>(Peekable<Chars<'a>>); impl<'a> Tokenizer<'a> { fn collect_number(&mut self, first_char: char) -> Token { let mut num = String::from(first_char); while let Some(&c @ '0'..='9') = self.0.peek() { num.push(c); self.0.next(); } Token::Number(num) } fn collect_identifier(&mut self, first_char: char) -> Token { let mut ident = String::from(first_char); while let Some(&c @ ('a'..='z' | '_' | '0'..='9')) = self.0.peek() { ident.push(c); self.0.next(); } Token::Identifier(ident) } } impl<'a> Iterator for Tokenizer<'a> { type Item = Token; fn next(&mut self) -> Option<Token> { let c = self.0.next()?; match c { '0'..='9' => Some(self.collect_number(c)), 'a'..='z' => Some(self.collect_identifier(c)), '+' => Some(Token::Operator(Op::Add)), '-' => Some(Token::Operator(Op::Sub)), _ => panic!("Caractere inesperado {c}"), } } } fn parse(input: &str) -> Expression { let mut tokens = tokenize(input); fn parse_expr<'a>(tokens: &mut Tokenizer<'a>) -> Expression { let Some(tok) = tokens.next() else { panic!("Fim de entrada inesperado"); }; let expr = match tok { Token::Number(num) => { let v = num.parse().expect("Inteiro de 32 bits invĂĄlido'"); Expression::Number(v) } Token::Identifier(ident) => Expression::Var(ident), Token::Operator(_) => panic!("Token inesperado {tok:?}"), }; // Olhe adiante para analisar uma operação binĂĄria, se presente. match tokens.next() { None => expr, Some(Token::Operator(op)) => Expression::Operation( Box::new(expr), op, Box::new(parse_expr(tokens)), ), Some(tok) => panic!("Token inesperado {tok:?}"), } } parse_expr(&mut tokens) } fn main() { let expr = parse("10+foo+20-30"); println!("{expr:?}"); }