mirror of https://github.com/rm-dr/daisy
Compare commits
5 Commits
36540f37b8
...
09996801d8
Author | SHA1 | Date |
---|---|---|
Mark | 09996801d8 | |
Mark | da1507e1fc | |
Mark | 7c54c1eb91 | |
Mark | e0ca8be79f | |
Mark | fc4c28543f |
|
@ -28,7 +28,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "daisycalc"
|
name = "daisycalc"
|
||||||
version = "1.0.0"
|
version = "1.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"rug",
|
"rug",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "daisycalc"
|
name = "daisycalc"
|
||||||
version = "1.0.0"
|
version = "1.0.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
build = "buildscript/main.rs"
|
build = "buildscript/main.rs"
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
|
|
|
@ -5,6 +5,7 @@ A high-precision scientific calculator with support for units, derivatives, and
|
||||||
Many features are missing, this is still under development.
|
Many features are missing, this is still under development.
|
||||||
|
|
||||||
# 📦 Installation
|
# 📦 Installation
|
||||||
|
- **Cargo:** `cargo install daisycalc`
|
||||||
- **Arch:** `yay -S daisy`
|
- **Arch:** `yay -S daisy`
|
||||||
- **Debian:** coming soon
|
- **Debian:** coming soon
|
||||||
|
|
||||||
|
|
11
TODO.md
11
TODO.md
|
@ -2,9 +2,9 @@
|
||||||
- update Cargo.toml
|
- update Cargo.toml
|
||||||
- run cargo test
|
- run cargo test
|
||||||
- commit
|
- commit
|
||||||
- git tag -a v1.0.0 -m "Version 1.0.0"
|
- push
|
||||||
- git push
|
- merge
|
||||||
- git push origin v1.0.0
|
- git tag -a v1.0.0 -m "Version 1.0.0" on merge commit
|
||||||
- cargo publish
|
- cargo publish
|
||||||
- Update packages
|
- Update packages
|
||||||
|
|
||||||
|
@ -27,11 +27,6 @@
|
||||||
- Optional history file
|
- Optional history file
|
||||||
- daisyrc file
|
- daisyrc file
|
||||||
- Compile to WASM, publish a webapp
|
- Compile to WASM, publish a webapp
|
||||||
- Options:
|
|
||||||
- disable replacement
|
|
||||||
- disable special characters
|
|
||||||
- 1/ as -1 power toggle
|
|
||||||
- powers as superscripts toggle
|
|
||||||
- evaluate straight from command line
|
- evaluate straight from command line
|
||||||
- Auto-push to crates.io
|
- Auto-push to crates.io
|
||||||
- Package for debian
|
- Package for debian
|
||||||
|
|
|
@ -170,7 +170,7 @@ pub fn do_command(
|
||||||
|
|
||||||
t.push(&format!(
|
t.push(&format!(
|
||||||
" {key}{padding} = [c]{v}[n]\n",
|
" {key}{padding} = [c]{v}[n]\n",
|
||||||
v = value.to_string(),
|
v = value.display(context),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,7 +184,7 @@ pub fn do_command(
|
||||||
|
|
||||||
t.push(&format!(
|
t.push(&format!(
|
||||||
" {s}{padding} = [c]{v}[n]\n",
|
" {s}{padding} = [c]{v}[n]\n",
|
||||||
v = exp.to_string(),
|
v = exp.display(context),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,68 @@ use crate::quantity::freeunit_from_string;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Config {
|
||||||
|
|
||||||
|
// How to color terminal text.
|
||||||
|
// 0: No colors
|
||||||
|
// 1: ANSI-compatible, 8 colors
|
||||||
|
// 2: Full 256 color and special styles
|
||||||
|
pub term_color_type: u8,
|
||||||
|
|
||||||
|
// Should we accept input and print in unicode?
|
||||||
|
//pub enable_unicode: bool,
|
||||||
|
|
||||||
|
// Should we replace certain strings (like "pi")
|
||||||
|
// with prettier unicode alternatives?
|
||||||
|
//
|
||||||
|
// Automatically disabled if enable_unicode is off.
|
||||||
|
//pub enable_substituion: bool,
|
||||||
|
|
||||||
|
// Should we print simple powers
|
||||||
|
// as unicode superscript chars?
|
||||||
|
//
|
||||||
|
// Automatically disables if enable_unicode is off.
|
||||||
|
pub enable_super_powers: bool,
|
||||||
|
|
||||||
|
// Should we write "one-over" fractions
|
||||||
|
// as -1 powers?
|
||||||
|
//
|
||||||
|
// Automatically disabled if enable_super_powers is off.
|
||||||
|
pub enable_one_over_power: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn new() -> Config {
|
||||||
|
Config{
|
||||||
|
term_color_type: 2,
|
||||||
|
//enable_substituion: true,
|
||||||
|
//enable_unicode: true,
|
||||||
|
enable_super_powers: true,
|
||||||
|
enable_one_over_power: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check(&mut self) {
|
||||||
|
//if !self.enable_unicode {
|
||||||
|
// self.enable_substituion = false;
|
||||||
|
// self.enable_super_powers = false;
|
||||||
|
//}
|
||||||
|
|
||||||
|
if !self.enable_super_powers {
|
||||||
|
self.enable_one_over_power = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
|
pub config: Config,
|
||||||
|
|
||||||
history: Vec<Expression>,
|
history: Vec<Expression>,
|
||||||
variables: HashMap<String, Expression>,
|
variables: HashMap<String, Expression>,
|
||||||
functions: HashMap<String, (Vec<String>, Expression)>,
|
functions: HashMap<String, (Vec<String>, Expression)>,
|
||||||
|
@ -16,6 +77,7 @@ pub struct Context {
|
||||||
impl Context {
|
impl Context {
|
||||||
pub fn new() -> Context {
|
pub fn new() -> Context {
|
||||||
Context{
|
Context{
|
||||||
|
config: Config::new(),
|
||||||
history: Vec::new(),
|
history: Vec::new(),
|
||||||
variables: HashMap::new(),
|
variables: HashMap::new(),
|
||||||
functions: HashMap::new(),
|
functions: HashMap::new(),
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use std::io::Write;
|
|
||||||
use std::io::stdout;
|
use std::io::stdout;
|
||||||
use std::io::stdin;
|
use std::io::stdin;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -6,19 +5,33 @@ use std::env;
|
||||||
use termion::{
|
use termion::{
|
||||||
event::Key,
|
event::Key,
|
||||||
input::TermRead,
|
input::TermRead,
|
||||||
raw::IntoRawMode
|
raw::IntoRawMode,
|
||||||
|
color::DetectColors
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::promptbuffer::PromptBuffer;
|
use super::promptbuffer::PromptBuffer;
|
||||||
use crate::command;
|
use crate::command;
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
|
use crate::FormattedText;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn main() -> Result<(), std::io::Error> {
|
pub fn main() -> Result<(), std::io::Error> {
|
||||||
let mut stdout = stdout().into_raw_mode().unwrap();
|
let mut stdout = stdout().into_raw_mode().unwrap();
|
||||||
let mut pb: PromptBuffer = PromptBuffer::new(64);
|
let mut pb: PromptBuffer = PromptBuffer::new(64);
|
||||||
let mut context: Context = Context::new();
|
let mut context = Context::new();
|
||||||
|
|
||||||
|
// Set color compatibilty
|
||||||
|
let term_colors = stdout.available_colors().unwrap_or(0);
|
||||||
|
if term_colors >= 256 {
|
||||||
|
context.config.term_color_type = 2
|
||||||
|
} else if term_colors >= 8 {
|
||||||
|
context.config.term_color_type = 1
|
||||||
|
} else {
|
||||||
|
context.config.term_color_type = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
context.config.check();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Handle command-line arguments
|
// Handle command-line arguments
|
||||||
|
@ -28,7 +41,21 @@ pub fn main() -> Result<(), std::io::Error> {
|
||||||
t.write(&context, &mut stdout)?;
|
t.write(&context, &mut stdout)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else if args.iter().any(|s| s == "--version") {
|
} else if args.iter().any(|s| s == "--version") {
|
||||||
write!(stdout, "Daisy v{}\r\n", env!("CARGO_PKG_VERSION"))?;
|
let t = FormattedText::new(format!(
|
||||||
|
"Daisy v{}\n", env!("CARGO_PKG_VERSION")
|
||||||
|
));
|
||||||
|
t.write(&context, &mut stdout)?;
|
||||||
|
return Ok(());
|
||||||
|
} else if args.iter().any(|s| s == "--debug") {
|
||||||
|
let t = FormattedText::new(format!(
|
||||||
|
concat!(
|
||||||
|
"Daisy v{}\n",
|
||||||
|
"Your terminal supports {} colors.\n"
|
||||||
|
),
|
||||||
|
env!("CARGO_PKG_VERSION"),
|
||||||
|
term_colors
|
||||||
|
));
|
||||||
|
t.write(&context, &mut stdout)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +72,7 @@ pub fn main() -> Result<(), std::io::Error> {
|
||||||
// while inside a substitution
|
// while inside a substitution
|
||||||
pb.write_prompt_nocursor(&mut context, &mut stdout)?;
|
pb.write_prompt_nocursor(&mut context, &mut stdout)?;
|
||||||
let in_str = pb.enter();
|
let in_str = pb.enter();
|
||||||
write!(stdout, "\r\n")?;
|
FormattedText::newline(&mut stdout)?;
|
||||||
if in_str == "" { break; }
|
if in_str == "" { break; }
|
||||||
|
|
||||||
if in_str.trim() == "quit" {
|
if in_str.trim() == "quit" {
|
||||||
|
@ -83,6 +110,6 @@ pub fn main() -> Result<(), std::io::Error> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(stdout, "\r\n")?;
|
FormattedText::newline(&mut stdout)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
|
@ -78,8 +78,8 @@ pub fn eval_operator(context: &mut Context, g: &Expression) -> Result<Option<Exp
|
||||||
return Err((
|
return Err((
|
||||||
*la + *lb + *op_loc,
|
*la + *lb + *op_loc,
|
||||||
DaisyError::IncompatibleUnits(
|
DaisyError::IncompatibleUnits(
|
||||||
a.convert_to_base().unit.to_string(),
|
a.convert_to_base().unit.display(context),
|
||||||
b.convert_to_base().unit.to_string()
|
b.convert_to_base().unit.display(context)
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -101,8 +101,8 @@ pub fn eval_operator(context: &mut Context, g: &Expression) -> Result<Option<Exp
|
||||||
return Err((
|
return Err((
|
||||||
*la + *lb + *op_loc,
|
*la + *lb + *op_loc,
|
||||||
DaisyError::IncompatibleUnits(
|
DaisyError::IncompatibleUnits(
|
||||||
a.convert_to_base().unit.to_string(),
|
a.convert_to_base().unit.display(context),
|
||||||
b.convert_to_base().unit.to_string()
|
b.convert_to_base().unit.display(context)
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -179,8 +179,8 @@ pub fn eval_operator(context: &mut Context, g: &Expression) -> Result<Option<Exp
|
||||||
return Err((
|
return Err((
|
||||||
*la + *lb + *op_loc,
|
*la + *lb + *op_loc,
|
||||||
DaisyError::IncompatibleUnits(
|
DaisyError::IncompatibleUnits(
|
||||||
va.convert_to_base().unit.to_string(),
|
va.convert_to_base().unit.display(context),
|
||||||
vb.convert_to_base().unit.to_string()
|
vb.convert_to_base().unit.display(context)
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use termion::style;
|
||||||
use termion::clear;
|
use termion::clear;
|
||||||
use termion::cursor;
|
use termion::cursor;
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
|
use crate::context::Context;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -18,8 +19,92 @@ impl ToString for FormattedText {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl FormattedText {
|
fn format_map_none(c: char) -> Option<String> {
|
||||||
|
Some(match c {
|
||||||
|
'n'|'i'|'t'|'a'|
|
||||||
|
'e'|'c'|'s'|'r'
|
||||||
|
=> { "".to_string() },
|
||||||
|
_ => { return None }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn format_map_ansi(c: char) -> Option<String> {
|
||||||
|
Some(match c {
|
||||||
|
'n' => { // Normal text
|
||||||
|
format!("{}{}", color::Fg(color::Reset), color::Bg(color::Reset))
|
||||||
|
},
|
||||||
|
'i' => { // Normal italic text
|
||||||
|
format!("{}{}", color::Fg(color::Reset), color::Bg(color::Reset))
|
||||||
|
},
|
||||||
|
't' => { // Title text
|
||||||
|
format!("{}{}", color::Fg(color::AnsiValue(6)), color::Bg(color::Reset))
|
||||||
|
},
|
||||||
|
'a' => { // Colored text
|
||||||
|
format!("{}{}", color::Fg(color::AnsiValue(5)), color::Bg(color::Reset))
|
||||||
|
},
|
||||||
|
'e' => { // Error titles
|
||||||
|
format!("{}{}", color::Fg(color::AnsiValue(1)), color::Bg(color::Reset))
|
||||||
|
},
|
||||||
|
'c' => { // Console text
|
||||||
|
format!("{}{}", color::Fg(color::AnsiValue(0)), color::Bg(color::AnsiValue(7)))
|
||||||
|
},
|
||||||
|
's' => { // Repeat prompt (how => is styled)
|
||||||
|
format!("{}{}", color::Fg(color::AnsiValue(2)), color::Bg(color::Reset))
|
||||||
|
},
|
||||||
|
'r' => { // Result prompt (how = is styled)
|
||||||
|
format!("{}{}", color::Fg(color::AnsiValue(4)), color::Bg(color::Reset))
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => { return None }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn format_map_full(c: char) -> Option<String> {
|
||||||
|
Some(match c {
|
||||||
|
'n' => { // Normal text
|
||||||
|
format!("{}{}", color::Fg(color::Reset), style::Reset)
|
||||||
|
},
|
||||||
|
'i' => { // Normal italic text
|
||||||
|
format!("{}{}", color::Fg(color::Reset), style::Italic)
|
||||||
|
},
|
||||||
|
't' => { // Title text
|
||||||
|
format!("{}{}", color::Fg(color::Magenta), style::Bold)
|
||||||
|
},
|
||||||
|
'a' => { // Colored text
|
||||||
|
format!("{}{}", color::Fg(color::Magenta), style::Reset)
|
||||||
|
},
|
||||||
|
'e' => { // Error titles
|
||||||
|
format!("{}{}", color::Fg(color::Red), style::Bold)
|
||||||
|
},
|
||||||
|
'c' => { // Console text
|
||||||
|
format!("{}{}", color::Fg(color::LightBlack), style::Italic)
|
||||||
|
},
|
||||||
|
's' => { // Repeat prompt (how => is styled)
|
||||||
|
format!("{}{}", color::Fg(color::Magenta), style::Bold)
|
||||||
|
},
|
||||||
|
'r' => { // Result prompt (how = is styled)
|
||||||
|
format!("{}{}", color::Fg(color::Green), style::Bold)
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => { return None }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl FormattedText {
|
||||||
|
pub fn newline(stdout: &mut RawTerminal<std::io::Stdout>) -> Result<(), std::io::Error> {
|
||||||
|
write!(
|
||||||
|
stdout,
|
||||||
|
"\r\n",
|
||||||
|
)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl FormattedText {
|
||||||
pub fn new(s: String) -> FormattedText {
|
pub fn new(s: String) -> FormattedText {
|
||||||
return FormattedText {
|
return FormattedText {
|
||||||
text: s
|
text: s
|
||||||
|
@ -58,30 +143,22 @@ impl FormattedText {
|
||||||
let b = chars.next().unwrap();
|
let b = chars.next().unwrap();
|
||||||
|
|
||||||
match (a, b) {
|
match (a, b) {
|
||||||
('n', ']') => { // Normal text
|
(c, ']') => { // Normal text
|
||||||
s.push_str(&format!("{}{}", color::Fg(color::Reset), style::Reset));
|
|
||||||
},
|
|
||||||
('i', ']') => { // Normal italic text
|
|
||||||
s.push_str(&format!("{}{}", color::Fg(color::Reset), style::Italic));
|
|
||||||
},
|
|
||||||
('t', ']') => { // Title text
|
|
||||||
s.push_str(&format!("{}{}", color::Fg(color::Magenta), style::Bold));
|
|
||||||
},
|
|
||||||
('a', ']') => { // Colored text
|
|
||||||
s.push_str(&format!("{}{}", color::Fg(color::Magenta), style::Reset));
|
|
||||||
},
|
|
||||||
('e', ']') => { // Error titles
|
|
||||||
s.push_str(&format!("{}{}", color::Fg(color::Red), style::Bold));
|
|
||||||
},
|
|
||||||
('c', ']') => { // Console text
|
|
||||||
s.push_str(&format!("{}{}", color::Fg(color::LightBlack), style::Italic));
|
|
||||||
},
|
|
||||||
|
|
||||||
('s', ']') => { // Repeat prompt (how => is styled)
|
let q = match context.config.term_color_type {
|
||||||
s.push_str(&format!("{}{}", color::Fg(color::Magenta), style::Bold));
|
0 => format_map_none(c),
|
||||||
},
|
1 => format_map_ansi(c),
|
||||||
('r', ']') => { // Result prompt (how = is styled)
|
2 => format_map_full(c),
|
||||||
s.push_str(&format!("{}{}", color::Fg(color::Green), style::Bold));
|
_ => unreachable!("Invalid term_color_type")
|
||||||
|
};
|
||||||
|
|
||||||
|
if q.is_some() {
|
||||||
|
s.push_str(&q.unwrap());
|
||||||
|
} else {
|
||||||
|
s.push('[');
|
||||||
|
s.push(a);
|
||||||
|
s.push(b);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -85,13 +85,13 @@ fn do_expression(
|
||||||
// Display parsed string
|
// Display parsed string
|
||||||
output.push(&format!(
|
output.push(&format!(
|
||||||
" [s]=>[n] {}\n\n",
|
" [s]=>[n] {}\n\n",
|
||||||
g.to_string()
|
g.display(context)
|
||||||
));
|
));
|
||||||
|
|
||||||
// Display result
|
// Display result
|
||||||
output.push(&format!(
|
output.push(&format!(
|
||||||
" [r]=[n] {}\n\n",
|
" [r]=[n] {}\n\n",
|
||||||
g_evaluated.to_string_outer(),
|
g_evaluated.display_outer(context),
|
||||||
));
|
));
|
||||||
|
|
||||||
return Ok((output, g_evaluated));
|
return Ok((output, g_evaluated));
|
||||||
|
@ -224,7 +224,7 @@ fn do_assignment(
|
||||||
// Display parsed string
|
// Display parsed string
|
||||||
output.push(&format!(
|
output.push(&format!(
|
||||||
" [s]=>[n] {left} = {}\n\n",
|
" [s]=>[n] {left} = {}\n\n",
|
||||||
g.to_string()
|
g.display(context)
|
||||||
));
|
));
|
||||||
|
|
||||||
// Evaluate expression with shadow variables
|
// Evaluate expression with shadow variables
|
||||||
|
@ -266,7 +266,7 @@ fn do_assignment(
|
||||||
// Display parsed string
|
// Display parsed string
|
||||||
output.push(&format!(
|
output.push(&format!(
|
||||||
" [t]=>[n] {left} = {}\n\n",
|
" [t]=>[n] {left} = {}\n\n",
|
||||||
g.to_string()
|
g.display(context)
|
||||||
));
|
));
|
||||||
|
|
||||||
// Evaluate expression
|
// Evaluate expression
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use crate::quantity::Quantity;
|
use crate::quantity::Quantity;
|
||||||
|
use crate::context::Context;
|
||||||
|
|
||||||
use super::Operator;
|
use super::Operator;
|
||||||
use super::Constant;
|
use super::Constant;
|
||||||
|
@ -17,17 +18,17 @@ pub enum Expression {
|
||||||
Tuple(LineLocation, VecDeque<Expression>),
|
Tuple(LineLocation, VecDeque<Expression>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for Expression {
|
impl Expression {
|
||||||
fn to_string(&self) -> String {
|
pub fn display(&self, context: &Context) -> String {
|
||||||
match self {
|
match self {
|
||||||
Expression::Quantity(_, v) => v.to_string(),
|
Expression::Quantity(_, v) => v.display(context),
|
||||||
Expression::Constant(_, c) => c.to_string(),
|
Expression::Constant(_, c) => c.to_string(),
|
||||||
Expression::Variable(_, s) => s.clone(),
|
Expression::Variable(_, s) => s.clone(),
|
||||||
Expression::Operator(_, o,a) => o.print(a),
|
Expression::Operator(_, o,a) => o.display(context, a),
|
||||||
Expression::Tuple(_, v) => {
|
Expression::Tuple(_, v) => {
|
||||||
format!("({})",
|
format!("({})",
|
||||||
v.iter()
|
v.iter()
|
||||||
.map(|x| x.to_string())
|
.map(|x| x.display(context))
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
)
|
)
|
||||||
|
@ -39,16 +40,16 @@ impl ToString for Expression {
|
||||||
impl Expression {
|
impl Expression {
|
||||||
// This is called only when this is the outermost Expression.
|
// This is called only when this is the outermost Expression.
|
||||||
// This sometimes leads to different--usually more verbose--behavior.
|
// This sometimes leads to different--usually more verbose--behavior.
|
||||||
pub fn to_string_outer(&self) -> String {
|
pub fn display_outer(&self, context: &Context) -> String {
|
||||||
match self {
|
match self {
|
||||||
Expression::Quantity(_, v) => v.to_string_outer(),
|
Expression::Quantity(_, v) => v.display_outer(context),
|
||||||
Expression::Constant(_, c) => c.to_string(),
|
Expression::Constant(_, c) => c.to_string(),
|
||||||
Expression::Variable(_, s) => s.clone(),
|
Expression::Variable(_, s) => s.clone(),
|
||||||
Expression::Operator(_, o,a) => o.print(a),
|
Expression::Operator(_, o,a) => o.display(context, a),
|
||||||
Expression::Tuple(_, v) => {
|
Expression::Tuple(_, v) => {
|
||||||
format!("({})",
|
format!("({})",
|
||||||
v.iter()
|
v.iter()
|
||||||
.map(|x| x.to_string())
|
.map(|x| x.display(context))
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
)
|
)
|
||||||
|
|
|
@ -128,8 +128,8 @@ impl Operator {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn add_parens_to_arg(&self, arg: &Expression) -> String {
|
fn add_parens_to_arg(&self, context: &Context, arg: &Expression) -> String {
|
||||||
let mut astr: String = arg.to_string();
|
let mut astr: String = arg.display(context);
|
||||||
if let Expression::Operator(_, o,_) = arg {
|
if let Expression::Operator(_, o,_) = arg {
|
||||||
if o.print_map() < self.print_map() {
|
if o.print_map() < self.print_map() {
|
||||||
astr = format!("({})", astr);
|
astr = format!("({})", astr);
|
||||||
|
@ -139,8 +139,8 @@ impl Operator {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn add_parens_to_arg_strict(&self, arg: &Expression) -> String {
|
fn add_parens_to_arg_strict(&self, context: &Context, arg: &Expression) -> String {
|
||||||
let mut astr: String = arg.to_string();
|
let mut astr: String = arg.display(context);
|
||||||
if let Expression::Operator(_, o,_) = arg {
|
if let Expression::Operator(_, o,_) = arg {
|
||||||
if o.print_map() <= self.print_map() {
|
if o.print_map() <= self.print_map() {
|
||||||
astr = format!("({})", astr);
|
astr = format!("({})", astr);
|
||||||
|
@ -150,56 +150,56 @@ impl Operator {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn print(&self, args: &VecDeque<Expression>) -> String {
|
pub fn display(&self, context: &Context, args: &VecDeque<Expression>) -> String {
|
||||||
match self {
|
match self {
|
||||||
Operator::Negative => {
|
Operator::Negative => {
|
||||||
return format!("-{}", self.add_parens_to_arg(&args[0]));
|
return format!("-{}", self.add_parens_to_arg(context, &args[0]));
|
||||||
},
|
},
|
||||||
|
|
||||||
Operator::Sqrt => {
|
Operator::Sqrt => {
|
||||||
return format!(
|
return format!(
|
||||||
"√{}",
|
"√{}",
|
||||||
self.add_parens_to_arg(&args[0]),
|
self.add_parens_to_arg(context, &args[0]),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
Operator::ModuloLong => {
|
Operator::ModuloLong => {
|
||||||
return format!(
|
return format!(
|
||||||
"{} mod {}",
|
"{} mod {}",
|
||||||
self.add_parens_to_arg(&args[0]),
|
self.add_parens_to_arg(context, &args[0]),
|
||||||
self.add_parens_to_arg(&args[1])
|
self.add_parens_to_arg(context, &args[1])
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
Operator::DivideLong => {
|
Operator::DivideLong => {
|
||||||
return format!(
|
return format!(
|
||||||
"{} per {}",
|
"{} per {}",
|
||||||
self.add_parens_to_arg(&args[0]),
|
self.add_parens_to_arg(context, &args[0]),
|
||||||
self.add_parens_to_arg(&args[1])
|
self.add_parens_to_arg(context, &args[1])
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
Operator::UnitConvert => {
|
Operator::UnitConvert => {
|
||||||
return format!(
|
return format!(
|
||||||
"{} to {}",
|
"{} to {}",
|
||||||
self.add_parens_to_arg(&args[0]),
|
self.add_parens_to_arg(context, &args[0]),
|
||||||
self.add_parens_to_arg(&args[1])
|
self.add_parens_to_arg(context, &args[1])
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
Operator::Modulo => {
|
Operator::Modulo => {
|
||||||
return format!(
|
return format!(
|
||||||
"{} % {}",
|
"{} % {}",
|
||||||
self.add_parens_to_arg(&args[0]),
|
self.add_parens_to_arg(context, &args[0]),
|
||||||
self.add_parens_to_arg(&args[1])
|
self.add_parens_to_arg(context, &args[1])
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
Operator::Subtract => {
|
Operator::Subtract => {
|
||||||
return format!(
|
return format!(
|
||||||
"{} - {}",
|
"{} - {}",
|
||||||
self.add_parens_to_arg(&args[0]),
|
self.add_parens_to_arg(context, &args[0]),
|
||||||
self.add_parens_to_arg(&args[1])
|
self.add_parens_to_arg(context, &args[1])
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -208,13 +208,13 @@ impl Operator {
|
||||||
let q = &args[1];
|
let q = &args[1];
|
||||||
|
|
||||||
if {
|
if {
|
||||||
//context.config.enable_super_powers &&
|
context.config.enable_super_powers &&
|
||||||
q.is_unitless_integer() &&
|
q.is_unitless_integer() &&
|
||||||
!q.to_string().contains("e")
|
!q.display(context).contains("e")
|
||||||
} {
|
} {
|
||||||
// Write integer powers as a superscript
|
// Write integer powers as a superscript
|
||||||
let mut b = String::new();
|
let mut b = String::new();
|
||||||
for c in q.to_string().chars() {
|
for c in q.display(context).chars() {
|
||||||
b.push(match c {
|
b.push(match c {
|
||||||
'-' => '⁻',
|
'-' => '⁻',
|
||||||
'0' => '⁰',
|
'0' => '⁰',
|
||||||
|
@ -233,27 +233,27 @@ impl Operator {
|
||||||
|
|
||||||
return format!(
|
return format!(
|
||||||
"{}{}",
|
"{}{}",
|
||||||
self.add_parens_to_arg_strict(&args[0]),
|
self.add_parens_to_arg_strict(context, &args[0]),
|
||||||
b
|
b
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return format!(
|
return format!(
|
||||||
"{}^{}",
|
"{}^{}",
|
||||||
self.add_parens_to_arg_strict(&args[0]),
|
self.add_parens_to_arg_strict(context, &args[0]),
|
||||||
self.add_parens_to_arg_strict(&args[1])
|
self.add_parens_to_arg_strict(context, &args[1])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Operator::Factorial => {
|
Operator::Factorial => {
|
||||||
return format!("{}!", self.add_parens_to_arg(&args[0]));
|
return format!("{}!", self.add_parens_to_arg(context, &args[0]));
|
||||||
},
|
},
|
||||||
|
|
||||||
Operator::Add => {
|
Operator::Add => {
|
||||||
return format!(
|
return format!(
|
||||||
"{} + {}",
|
"{} + {}",
|
||||||
self.add_parens_to_arg(&args[0]),
|
self.add_parens_to_arg(context, &args[0]),
|
||||||
self.add_parens_to_arg(&args[1])
|
self.add_parens_to_arg(context, &args[1])
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -283,26 +283,26 @@ impl Operator {
|
||||||
if let Expression::Quantity(_, u) = b {
|
if let Expression::Quantity(_, u) = b {
|
||||||
if u.unit.no_space() {
|
if u.unit.no_space() {
|
||||||
return format!("{}{}",
|
return format!("{}{}",
|
||||||
self.add_parens_to_arg_strict(a),
|
self.add_parens_to_arg_strict(context, a),
|
||||||
self.add_parens_to_arg_strict(b)
|
self.add_parens_to_arg_strict(context, b)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return format!("{} {}",
|
return format!("{} {}",
|
||||||
self.add_parens_to_arg_strict(a),
|
self.add_parens_to_arg_strict(context, a),
|
||||||
self.add_parens_to_arg_strict(b)
|
self.add_parens_to_arg_strict(context, b)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return format!("{}{}",
|
return format!("{}{}",
|
||||||
self.add_parens_to_arg_strict(a),
|
self.add_parens_to_arg_strict(context, a),
|
||||||
self.add_parens_to_arg_strict(b)
|
self.add_parens_to_arg_strict(context, b)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return format!("{} × {}",
|
return format!("{} × {}",
|
||||||
self.add_parens_to_arg_strict(a),
|
self.add_parens_to_arg_strict(context, a),
|
||||||
self.add_parens_to_arg_strict(b)
|
self.add_parens_to_arg_strict(context, b)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -313,25 +313,25 @@ impl Operator {
|
||||||
|
|
||||||
|
|
||||||
if let Expression::Quantity(_, q) = a {
|
if let Expression::Quantity(_, q) = a {
|
||||||
if q.is_one() {
|
if q.is_one() && context.config.enable_one_over_power {
|
||||||
return format!("{}⁻¹",
|
return format!("{}⁻¹",
|
||||||
self.add_parens_to_arg_strict(b)
|
self.add_parens_to_arg_strict(context, b)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return format!("{} ÷ {}",
|
return format!("{} ÷ {}",
|
||||||
self.add_parens_to_arg_strict(a),
|
self.add_parens_to_arg_strict(context, a),
|
||||||
self.add_parens_to_arg_strict(b)
|
self.add_parens_to_arg_strict(context, b)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
Operator::Function(s) => {
|
Operator::Function(s) => {
|
||||||
return format!("{}({})", s.to_string(), args[0].to_string());
|
return format!("{}({})", s.to_string(), args[0].display(context));
|
||||||
},
|
},
|
||||||
|
|
||||||
Operator::UserFunction(s) => {
|
Operator::UserFunction(s) => {
|
||||||
return format!("{}({})", s.to_string(), args[0].to_string());
|
return format!("{}({})", s, args[0].display(context));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ use std::ops::{
|
||||||
};
|
};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
use crate::context::Context;
|
||||||
use crate::quantity::Unit;
|
use crate::quantity::Unit;
|
||||||
use crate::quantity::FreeUnit;
|
use crate::quantity::FreeUnit;
|
||||||
|
|
||||||
|
@ -21,12 +22,12 @@ pub struct Quantity {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl ToString for Quantity {
|
impl Quantity {
|
||||||
fn to_string(&self) -> String {
|
pub fn display(&self, context: &Context) -> String {
|
||||||
let n = self.scalar.to_string();
|
let n = self.scalar.to_string();
|
||||||
if self.unitless() { return n; }
|
if self.unitless() { return n; }
|
||||||
|
|
||||||
let u = self.unit.to_string();
|
let u = self.unit.display(context);
|
||||||
if self.is_one() { return u; };
|
if self.is_one() { return u; };
|
||||||
|
|
||||||
if self.unit.no_space() {
|
if self.unit.no_space() {
|
||||||
|
@ -38,11 +39,11 @@ impl ToString for Quantity {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Quantity {
|
impl Quantity {
|
||||||
pub fn to_string_outer(&self) -> String {
|
pub fn display_outer(&self, context: &Context) -> String {
|
||||||
let n = self.scalar.to_string();
|
let n = self.scalar.to_string();
|
||||||
if self.unitless() { return n; }
|
if self.unitless() { return n; }
|
||||||
|
|
||||||
let u = self.unit.to_string();
|
let u = self.unit.display(context);
|
||||||
if self.unit.no_space() {
|
if self.unit.no_space() {
|
||||||
return format!("{n}{u}");
|
return format!("{n}{u}");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::ops::{
|
||||||
MulAssign, DivAssign
|
MulAssign, DivAssign
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::context::Context;
|
||||||
use crate::quantity::Scalar;
|
use crate::quantity::Scalar;
|
||||||
use crate::quantity::Quantity;
|
use crate::quantity::Quantity;
|
||||||
use super::FreeUnit;
|
use super::FreeUnit;
|
||||||
|
@ -16,8 +17,8 @@ pub struct Unit {
|
||||||
pub val: HashMap<FreeUnit, Scalar>
|
pub val: HashMap<FreeUnit, Scalar>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for Unit {
|
impl Unit {
|
||||||
fn to_string(&self) -> String {
|
pub fn display(&self, context: &Context) -> String {
|
||||||
|
|
||||||
if self.unitless() { return String::new(); };
|
if self.unitless() { return String::new(); };
|
||||||
|
|
||||||
|
@ -37,7 +38,11 @@ impl ToString for Unit {
|
||||||
|
|
||||||
if *p == Scalar::new_rational(1f64).unwrap() {
|
if *p == Scalar::new_rational(1f64).unwrap() {
|
||||||
t.push_str(&format!("{c}·"));
|
t.push_str(&format!("{c}·"));
|
||||||
} else if p.is_int() && !p.to_string().contains("e"){
|
} else if {
|
||||||
|
context.config.enable_super_powers &&
|
||||||
|
p.is_int() &&
|
||||||
|
!p.to_string().contains("e")
|
||||||
|
} {
|
||||||
t.push_str(&c);
|
t.push_str(&c);
|
||||||
for c in p.to_string().chars() {
|
for c in p.to_string().chars() {
|
||||||
t.push( match c {
|
t.push( match c {
|
||||||
|
@ -74,7 +79,11 @@ impl ToString for Unit {
|
||||||
bottom_count += 1;
|
bottom_count += 1;
|
||||||
if t.len() != 0 && *p == Scalar::new_rational(-1f64).unwrap() {
|
if t.len() != 0 && *p == Scalar::new_rational(-1f64).unwrap() {
|
||||||
b.push_str(&format!("{c}·"));
|
b.push_str(&format!("{c}·"));
|
||||||
} else if p.is_int() && !p.to_string().contains("e") {
|
} else if {
|
||||||
|
context.config.enable_super_powers &&
|
||||||
|
p.is_int() &&
|
||||||
|
!p.to_string().contains("e")
|
||||||
|
} {
|
||||||
b.push_str(&c);
|
b.push_str(&c);
|
||||||
for c in p.to_string().chars() {
|
for c in p.to_string().chars() {
|
||||||
if c == '-' && t.len() != 0 { continue; }
|
if c == '-' && t.len() != 0 { continue; }
|
||||||
|
|
|
@ -10,8 +10,10 @@ fn eval_to_str(s: &str) -> Result<String, ()> {
|
||||||
};
|
};
|
||||||
//let out_str = g.print();
|
//let out_str = g.print();
|
||||||
|
|
||||||
return match evaluate(&mut Context::new(), &g) {
|
let mut c = Context::new();
|
||||||
Ok(x) => Ok(x.to_string_outer()),
|
|
||||||
|
return match evaluate(&mut c, &g) {
|
||||||
|
Ok(x) => Ok(x.display_outer(&c)),
|
||||||
Err(_) => Err(())
|
Err(_) => Err(())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue