Rust · 5107 bytes Raw Blame History
1 //! garcas - Standalone CAS command-line interface
2 //!
3 //! A REPL and expression evaluator using the garcalc-cas engine.
4 //!
5 //! Usage:
6 //! garcas # Start interactive REPL
7 //! garcas -e "2+2" # Evaluate expression
8 //! garcas --exact # Use exact/symbolic mode
9
10 use std::io::{self, BufRead, Write};
11
12 use anyhow::{Context, Result};
13 use clap::Parser;
14 use garcalc_cas::{Evaluator, eval::AngleMode};
15 use tracing_subscriber::EnvFilter;
16
17 #[derive(Parser)]
18 #[command(name = "garcas")]
19 #[command(about = "Computer Algebra System CLI")]
20 #[command(version)]
21 struct Args {
22 /// Expression to evaluate (if not provided, starts REPL)
23 #[arg(short, long)]
24 expr: Option<String>,
25
26 /// Use exact/symbolic mode (keep undefined symbols)
27 #[arg(long)]
28 exact: bool,
29
30 /// Angle mode: radians or degrees
31 #[arg(long, default_value = "radians")]
32 angle: String,
33
34 /// Suppress prompts (for scripting)
35 #[arg(short, long)]
36 quiet: bool,
37 }
38
39 fn main() -> Result<()> {
40 tracing_subscriber::fmt()
41 .with_env_filter(
42 EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warn")),
43 )
44 .init();
45
46 let args = Args::parse();
47
48 let angle_mode = match args.angle.to_lowercase().as_str() {
49 "deg" | "degrees" => AngleMode::Degrees,
50 _ => AngleMode::Radians,
51 };
52
53 let evaluator = Evaluator::new()
54 .with_exact_mode(args.exact)
55 .with_angle_mode(angle_mode);
56
57 if let Some(expr) = args.expr {
58 // Single expression mode
59 let result = evaluate(&evaluator, &expr)?;
60 println!("{result}");
61 } else {
62 // REPL mode
63 repl(evaluator, args.quiet)?;
64 }
65
66 Ok(())
67 }
68
69 fn evaluate(evaluator: &Evaluator, input: &str) -> Result<String> {
70 let expr = garcalc_cas::parser::parse(input).context("parse error")?;
71 let result = evaluator.eval(&expr).context("evaluation error")?;
72 Ok(result.to_string())
73 }
74
75 fn repl(mut evaluator: Evaluator, quiet: bool) -> Result<()> {
76 let stdin = io::stdin();
77 let mut stdout = io::stdout();
78
79 if !quiet {
80 println!("garcas - Computer Algebra System");
81 println!("Type 'help' for commands, 'quit' to exit\n");
82 }
83
84 for line in stdin.lock().lines() {
85 let line = line?;
86 let input = line.trim();
87
88 if input.is_empty() {
89 continue;
90 }
91
92 // Handle commands
93 match input.to_lowercase().as_str() {
94 "quit" | "exit" | "q" => break,
95 "help" | "?" => {
96 print_help();
97 continue;
98 }
99 "clear" => {
100 evaluator.clear_vars();
101 if !quiet {
102 println!("Variables cleared");
103 }
104 continue;
105 }
106 "vars" => {
107 println!("(variable listing not yet implemented)");
108 continue;
109 }
110 _ => {}
111 }
112
113 // Check for variable assignment: x = expr
114 if let Some((name, value_str)) = input.split_once(":=") {
115 let name = name.trim();
116 let value_str = value_str.trim();
117 match evaluate(&evaluator, value_str) {
118 Ok(value) => {
119 if let Ok(expr) = garcalc_cas::parser::parse(&value) {
120 evaluator.set_var(name, expr);
121 if !quiet {
122 println!("{name} := {value}");
123 }
124 }
125 }
126 Err(e) => eprintln!("Error: {e}"),
127 }
128 continue;
129 }
130
131 // Evaluate expression
132 match evaluate(&evaluator, input) {
133 Ok(result) => println!("{result}"),
134 Err(e) => eprintln!("Error: {e}"),
135 }
136
137 if !quiet {
138 print!("> ");
139 stdout.flush()?;
140 }
141 }
142
143 Ok(())
144 }
145
146 fn print_help() {
147 println!(
148 r#"
149 Commands:
150 quit, exit, q Exit the REPL
151 help, ? Show this help
152 clear Clear all variables
153 vars List defined variables
154
155 Variable assignment:
156 x := 5 Assign value to variable
157 f := x^2 + 1 Define expression
158
159 Operators:
160 +, -, *, / Basic arithmetic
161 ^ Power/exponentiation
162 ! Factorial
163
164 Functions:
165 Trigonometric: sin, cos, tan, asin, acos, atan
166 Hyperbolic: sinh, cosh, tanh, asinh, acosh, atanh
167 Exponential: exp, ln, log, log10, log2
168 Roots: sqrt, cbrt
169 Other: abs, floor, ceil, round, sign
170 Number theory: factorial, gcd, lcm
171 Aggregation: min, max
172
173 Constants:
174 pi, e, i, infinity
175
176 Calculus (symbolic):
177 diff(expr, x) Derivative
178 diff(expr, x, 2) Second derivative
179 integrate(expr, x) Indefinite integral
180 integrate(expr, x, a, b) Definite integral
181 lim(expr, x, a) Limit
182 sum(expr, n, a, b) Summation
183 product(expr, n, a, b) Product
184
185 Examples:
186 2 + 3 * 4
187 sin(pi/2)
188 diff(x^2, x)
189 5!
190 sqrt(-1)
191 "#
192 );
193 }
194