Exercise: Rewriting with Result
The following implements a very simple parser for an expression language. However, it handles errors by panicking. Rewrite it to instead use idiomatic error handling and propagate errors to a return from main. Feel free to use thiserror and anyhow.
Hint: start by fixing error handling in the
parsefunction. Once that is working correctly, updateTokenizerto implementIterator<Item=Result<Token, TokenizerError>>and handle that in the parser.
1. ```
use std::iter::Peekable;
```
2. ```
use std::str::Chars;
```
4. ```
/// An arithmetic operator.
```
5. ```
#[derive(Debug, PartialEq, Clone, Copy)]
```
6. ```
enum Op {
```
7. ```
Add,
```
8. ```
Sub,
```
9. ```
}
```
11. ```
/// A token in the expression language.
```
12. ```
#[derive(Debug, PartialEq)]
```
13. ```
enum Token {
```
14. ```
Number(String),
```
15. ```
Identifier(String),
```
16. ```
Operator(Op),
```
17. ```
}
```
19. ```
/// An expression in the expression language.
```
20. ```
#[derive(Debug, PartialEq)]
```
21. ```
enum Expression {
```
22. ```
/// A reference to a variable.
```
23. ```
Var(String),
```
24. ```
/// A literal number.
```
25. ```
Number(u32),
```
26. ```
/// A binary operation.
```
27. ```
Operation(Box<Expression>, Op, Box<Expression>),
```
28. ```
}
```
30. ```
fn tokenize(input: &str) -> Tokenizer {
```
31. ```
return Tokenizer(input.chars().peekable());
```
32. ```
}
```
34. ```
struct Tokenizer<'a>(Peekable<Chars<'a>>);
```
36. ```
impl<'a> Tokenizer<'a> {
```
37. ```
fn collect_number(&mut self, first_char: char) -> Token {
```
38. ```
let mut num = String::from(first_char);
```
39. ```
while let Some(&c @ '0'..='9') = self.0.peek() {
```
40. ```
num.push(c);
```
41. ```
self.0.next();
```
42. ```
}
```
43. ```
Token::Number(num)
```
44. ```
}
```
46. ```
fn collect_identifier(&mut self, first_char: char) -> Token {
```
47. ```
let mut ident = String::from(first_char);
```
48. ```
while let Some(&c @ ('a'..='z' | '_' | '0'..='9')) = self.0.peek() {
```
49. ```
ident.push(c);
```
50. ```
self.0.next();
```
51. ```
}
```
52. ```
Token::Identifier(ident)
```
53. ```
}
```
54. ```
}
```
56. ```
impl<'a> Iterator for Tokenizer<'a> {
```
57. ```
type Item = Token;
```
59. ```
fn next(&mut self) -> Option<Token> {
```
60. ```
let c = self.0.next()?;
```
61. ```
match c {
```
62. ```
'0'..='9' => Some(self.collect_number(c)),
```
63. ```
'a'..='z' => Some(self.collect_identifier(c)),
```
64. ```
'+' => Some(Token::Operator(Op::Add)),
```
65. ```
'-' => Some(Token::Operator(Op::Sub)),
```
66. ```
_ => panic!("Unexpected character {c}"),
```
67. ```
}
```
68. ```
}
```
69. ```
}
```
71. ```
fn parse(input: &str) -> Expression {
```
72. ```
let mut tokens = tokenize(input);
```
74. ```
fn parse_expr<'a>(tokens: &mut Tokenizer<'a>) -> Expression {
```
75. ```
let Some(tok) = tokens.next() else {
```
76. ```
panic!("Unexpected end of input");
```
77. ```
};
```
78. ```
let expr = match tok {
```
79. ```
Token::Number(num) => {
```
80. ```
let v = num.parse().expect("Invalid 32-bit integer");
```
81. ```
Expression::Number(v)
```
82. ```
}
```
83. ```
Token::Identifier(ident) => Expression::Var(ident),
```
84. ```
Token::Operator(_) => panic!("Unexpected token {tok:?}"),
```
85. ```
};
```
86. ```
// Look ahead to parse a binary operation if present.
```
87. ```
match tokens.next() {
```
88. ```
None => expr,
```
89. ```
Some(Token::Operator(op)) => Expression::Operation(
```
90. ```
Box::new(expr),
```
91. ```
op,
```
92. ```
Box::new(parse_expr(tokens)),
```
93. ```
),
```
94. ```
Some(tok) => panic!("Unexpected token {tok:?}"),
```
95. ```
}
```
96. ```
}
```
98. ```
parse_expr(&mut tokens)
```
99. ```
}
```
101. ```
fn main() {
```
102. ```
let expr = parse("10+foo+20-30");
```
103. ```
println!("{expr:?}");
```
104. ```
}
```
