mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-06-08 02:26:14 +02:00
212 lines
6.1 KiB
Rust
212 lines
6.1 KiB
Rust
|
// Copyright 2023 Google LLC
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
// ANCHOR: solution
|
||
|
use thiserror::Error;
|
||
|
// ANCHOR: types
|
||
|
use std::iter::Peekable;
|
||
|
use std::str::Chars;
|
||
|
|
||
|
/// An arithmetic operator.
|
||
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||
|
enum Op {
|
||
|
Add,
|
||
|
Sub,
|
||
|
}
|
||
|
|
||
|
/// A token in the expression language.
|
||
|
#[derive(Debug, PartialEq)]
|
||
|
enum Token {
|
||
|
Number(String),
|
||
|
Identifier(String),
|
||
|
Operator(Op),
|
||
|
}
|
||
|
|
||
|
/// An expression in the expression language.
|
||
|
#[derive(Debug, PartialEq)]
|
||
|
enum Expression {
|
||
|
/// A reference to a variable.
|
||
|
Var(String),
|
||
|
/// A literal number.
|
||
|
Number(u32),
|
||
|
/// A binary operation.
|
||
|
Operation(Box<Expression>, Op, Box<Expression>),
|
||
|
}
|
||
|
// ANCHOR_END: types
|
||
|
|
||
|
fn tokenize(input: &str) -> Tokenizer {
|
||
|
return Tokenizer(input.chars().peekable());
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, Error)]
|
||
|
enum TokenizerError {
|
||
|
#[error("Unexpected character '{0}' in input")]
|
||
|
UnexpectedCharacter(char),
|
||
|
}
|
||
|
|
||
|
struct Tokenizer<'a>(Peekable<Chars<'a>>);
|
||
|
|
||
|
impl<'a> Iterator for Tokenizer<'a> {
|
||
|
type Item = Result<Token, TokenizerError>;
|
||
|
|
||
|
fn next(&mut self) -> Option<Result<Token, TokenizerError>> {
|
||
|
let Some(c) = self.0.next() else {
|
||
|
return None;
|
||
|
};
|
||
|
match c {
|
||
|
'0'..='9' => {
|
||
|
let mut num = String::from(c);
|
||
|
while let Some(c @ '0'..='9') = self.0.peek() {
|
||
|
num.push(*c);
|
||
|
self.0.next();
|
||
|
}
|
||
|
Some(Ok(Token::Number(num)))
|
||
|
}
|
||
|
'a'..='z' => {
|
||
|
let mut ident = String::from(c);
|
||
|
while let Some(c @ 'a'..='z' | c @ '_' | c @ '0'..='9') = self.0.peek() {
|
||
|
ident.push(*c);
|
||
|
self.0.next();
|
||
|
}
|
||
|
Some(Ok(Token::Identifier(ident)))
|
||
|
}
|
||
|
'+' => Some(Ok(Token::Operator(Op::Add))),
|
||
|
'-' => Some(Ok(Token::Operator(Op::Sub))),
|
||
|
_ => Some(Err(TokenizerError::UnexpectedCharacter(c))),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, Error)]
|
||
|
enum ParserError {
|
||
|
#[error("Tokenizer error: {0}")]
|
||
|
TokenizerError(#[from] TokenizerError),
|
||
|
#[error("Unexpected end of input")]
|
||
|
UnexpectedEOF,
|
||
|
#[error("Unexpected token {0:?}")]
|
||
|
UnexpectedToken(Token),
|
||
|
#[error("Invalid number")]
|
||
|
InvalidNumber(#[from] std::num::ParseIntError),
|
||
|
}
|
||
|
|
||
|
fn parse(input: &str) -> Result<Expression, ParserError> {
|
||
|
let mut tokens = tokenize(input);
|
||
|
|
||
|
fn parse_expr<'a>(tokens: &mut Tokenizer<'a>) -> Result<Expression, ParserError> {
|
||
|
let Some(tok) = tokens.next().transpose()? else {
|
||
|
return Err(ParserError::UnexpectedEOF);
|
||
|
};
|
||
|
let expr = match tok {
|
||
|
Token::Number(num) => {
|
||
|
let v = num.parse()?;
|
||
|
Expression::Number(v)
|
||
|
}
|
||
|
Token::Identifier(ident) => Expression::Var(ident),
|
||
|
Token::Operator(_) => return Err(ParserError::UnexpectedToken(tok)),
|
||
|
};
|
||
|
// Look ahead to parse a binary operation if present.
|
||
|
Ok(match tokens.next() {
|
||
|
None => expr,
|
||
|
Some(Ok(Token::Operator(op))) => {
|
||
|
Expression::Operation(Box::new(expr), op, Box::new(parse_expr(tokens)?))
|
||
|
}
|
||
|
Some(Err(e)) => return Err(e.into()),
|
||
|
Some(Ok(tok)) => return Err(ParserError::UnexpectedToken(tok)),
|
||
|
})
|
||
|
}
|
||
|
|
||
|
parse_expr(&mut tokens)
|
||
|
}
|
||
|
|
||
|
fn main() -> anyhow::Result<()> {
|
||
|
let expr = parse("10+foo+20-30")?;
|
||
|
println!("{expr:?}");
|
||
|
Ok(())
|
||
|
}
|
||
|
// ANCHOR_END: solution
|
||
|
|
||
|
/*
|
||
|
// ANCHOR: panics
|
||
|
fn tokenize(input: &str) -> Tokenizer {
|
||
|
return Tokenizer(input.chars().peekable());
|
||
|
}
|
||
|
|
||
|
struct Tokenizer<'a>(Peekable<Chars<'a>>);
|
||
|
|
||
|
impl<'a> Iterator for Tokenizer<'a> {
|
||
|
type Item = Token;
|
||
|
|
||
|
fn next(&mut self) -> Option<Token> {
|
||
|
let Some(c) = self.0.next() else {
|
||
|
return None;
|
||
|
};
|
||
|
match c {
|
||
|
'0'..='9' => {
|
||
|
let mut num = String::from(c);
|
||
|
while let Some(c @ '0'..='9') = self.0.peek() {
|
||
|
num.push(*c);
|
||
|
self.0.next();
|
||
|
}
|
||
|
Some(Token::Number(num))
|
||
|
}
|
||
|
'a'..='z' => {
|
||
|
let mut ident = String::from(c);
|
||
|
while let Some(c @ 'a'..='z' | c @ '_' | c @ '0'..='9') = self.0.peek() {
|
||
|
ident.push(*c);
|
||
|
self.0.next();
|
||
|
}
|
||
|
Some(Token::Identifier(ident))
|
||
|
}
|
||
|
'+' => Some(Token::Operator(Op::Add)),
|
||
|
'-' => Some(Token::Operator(Op::Sub)),
|
||
|
_ => panic!("Unexpected character {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!("Unexpected end of input");
|
||
|
};
|
||
|
let expr = match tok {
|
||
|
Token::Number(num) => {
|
||
|
let v = num.parse().expect("Invalid 32-bit integer'");
|
||
|
Expression::Number(v)
|
||
|
}
|
||
|
Token::Identifier(ident) => Expression::Var(ident),
|
||
|
Token::Operator(_) => panic!("Unexpected token {tok:?}"),
|
||
|
};
|
||
|
// Look ahead to parse a binary operation if present.
|
||
|
match tokens.next() {
|
||
|
None => expr,
|
||
|
Some(Token::Operator(op)) => {
|
||
|
Expression::Operation(Box::new(expr), op, Box::new(parse_expr(tokens)))
|
||
|
}
|
||
|
Some(tok) => panic!("Unexpected token {tok:?}"),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
parse_expr(&mut tokens)
|
||
|
}
|
||
|
|
||
|
fn main() {
|
||
|
let expr = parse("10+foo+20-30");
|
||
|
println!("{expr:?}");
|
||
|
}
|
||
|
// ANCHOR_END: panics
|
||
|
*/
|