1 use std::fmt;
2 use std::process::exit;
3 
4 use std::io::{self, stdout, BufRead, Write};
5 
6 use calc::{eval, eval_polish, eval_polish_with_env, eval_with_env, CalcError};
7 
8 use clap::{App, Arg};
9 
10 use liner::Context;
11 
12 const PROMPT: &'static str = "[]> ";
13 
prompt<W: Write>(out: &mut W) -> io::Result<()>14 pub fn prompt<W: Write>(out: &mut W) -> io::Result<()> {
15     write!(out, "{}", PROMPT)?;
16     out.flush()
17 }
18 
19 pub enum RuntimeError {
20     Calc(CalcError),
21     IO(io::Error),
22 }
23 
24 impl From<CalcError> for RuntimeError {
from(data: CalcError) -> RuntimeError25     fn from(data: CalcError) -> RuntimeError {
26         RuntimeError::Calc(data)
27     }
28 }
29 
30 impl From<io::Error> for RuntimeError {
from(data: io::Error) -> RuntimeError31     fn from(data: io::Error) -> RuntimeError {
32         RuntimeError::IO(data)
33     }
34 }
35 
36 impl fmt::Display for RuntimeError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result37     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38         match *self {
39             RuntimeError::Calc(ref c) => write!(f, "calc: {}", c),
40             RuntimeError::IO(ref e) => write!(f, "calc: {}", e),
41         }
42     }
43 }
44 
calc() -> Result<(), RuntimeError>45 pub fn calc() -> Result<(), RuntimeError> {
46     let stdout = stdout();
47     let mut stdout = stdout.lock();
48 
49     let matches = App::new("calc")
50         .version(env!("CARGO_PKG_VERSION"))
51         .author("Hunter Goldstein <hunter.d.goldstein@gmail.com>")
52         .about("Floating point calculator")
53         .set_term_width(80)
54         .arg(Arg::with_name("polish")
55              .short("p")
56              .long("polish")
57              .help("Parse expressions using polish notation versus infix notation"))
58         .arg(Arg::with_name("expr")
59              .help("Expression to evaluate by this program. If this argument is missing, enter interactive mode.")
60              .multiple(true)
61              .value_name("EXPR"))
62         .get_matches();
63 
64     // Check if the polish notation flag was given.
65     let polish = matches.is_present("polish");
66 
67     macro_rules! eval {
68         ($expr:expr) => {
69             match polish {
70                 true => eval_polish($expr)?,
71                 false => eval($expr)?,
72             }
73         };
74     }
75     macro_rules! eval_with_env {
76         ($expr:expr, $env:expr) => {
77             match polish {
78                 true => eval_polish_with_env($expr, $env)?,
79                 false => eval_with_env($expr, $env)?,
80             }
81         };
82     }
83 
84     match matches.values_of("expr") {
85         Some(values) => {
86             writeln!(
87                 stdout,
88                 "{}",
89                 eval!(&values.fold(String::new(), |acc, s| acc + s)),
90             )?;
91         }
92         None => {
93             if atty::is(atty::Stream::Stdin) {
94                 let mut con = Context::new();
95                 let mut ans = None;
96                 loop {
97                     let line = con.read_line(PROMPT, &mut |_| {})?;
98                     match line.trim() {
99                         "" => (),
100                         "exit" => break,
101                         s => {
102                             let mut env =
103                                 calc::parse::DefaultEnvironment::with_ans(ans);
104                             let evaluated = eval_with_env!(s, &mut env);
105                             writeln!(stdout, "{}", evaluated)?;
106                             ans = Some(evaluated);
107                         }
108                     }
109                     con.history.push(line.into())?;
110                 }
111             } else {
112                 let stdin = io::stdin();
113                 let lock = stdin.lock();
114                 for line in lock.lines() {
115                     writeln!(stdout, "{}", eval!(&line?))?;
116                 }
117             }
118         }
119     }
120     Ok(())
121 }
122 
main()123 fn main() {
124     let code = match calc() {
125         Ok(()) => 0,
126         Err(e) => {
127             println!("{}", e);
128             1
129         }
130     };
131     exit(code)
132 }
133