前回の続き。
逆ポーランド記法の計算処理を実装します。
use clap::Parser;
use std::fs::File;
use std::io::{stdin, BufRead, BufReader};
#[derive(Parser, Debug)]
#[clap(
name = "My RPM program",
version = "1.0.0",
author = "Your name",
about = "Super awesome sample RPM calculator"
)]
struct Opts {
#[clap(short, long)]
verbose: bool,
#[clap(name = "FILE")]
formula_file: Option<String>,
}
struct RpnCalculator(bool);
impl RpnCalculator {
pub fn new(verbose: bool) -> Self {
Self(verbose)
}
pub fn eval(&self, formula: &str) -> i32 {
let mut tokens = formula.split_whitespace().rev().collect::<Vec<_>>();
self.eval_inner(&mut tokens)
}
fn eval_inner(&self, tokens: &mut Vec<&str>) -> i32 {
let mut stack = Vec::new();
while let Some(token) = tokens.pop() {
if let Ok(x) = token.parse::<i32>() {
stack.push(x);
} else {
let y = stack.pop().expect("invalid syntax");
let x = stack.pop().expect("invalid syntax");
let res = match token {
"+" => x + y,
"-" => x - y,
"*" => x * y,
"/" => x / y,
"%" => x % y,
_ => panic!("invalid token"),
};
stack.push(res);
}
if self.0 {
println!("{:?} {:?}", tokens, stack);
}
}
if stack.len() == 1 {
stack[0]
} else {
panic!("invalid syntax")
}
}
}
fn main() {
let opts = Opts::parse();
if let Some(path) = opts.formula_file {
// ファイルから読み出す
let f = File::open(path).unwrap();
let reader = BufReader::new(f);
run(reader, opts.verbose);
} else {
// 標準入力から読み出す
let stdin = stdin();
let reader = stdin.lock();
run(reader, opts.verbose);
}
}
fn run<R: BufRead>(reader: R, verbose: bool) {
let calc = RpnCalculator::new(verbose);
for line in reader.lines() {
let line = line.unwrap();
let answer = calc.eval(&line);
println!("{}", answer);
}
}
$ cargo run -- -v
Compiling samplecli v0.1.0 (/home/taki/rust/samplecli)
Finished dev [unoptimized + debuginfo] target(s) in 0.76s
Running `target/debug/samplecli -v`
1 1 1 + +
["+", "+", "1", "1"] [1]
["+", "+", "1"] [1, 1]
["+", "+"] [1, 1, 1]
["+"] [1, 2]
[] [3]
3