gardesk/garcalc / 8905be0

Browse files

add garcas standalone CAS REPL

Authored by espadonne
SHA
8905be06ceaf2e61a146b03f5f1fdfded19e92ea
Parents
e8ccf3f
Tree
039ef9f

2 changed files

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