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