mirror of https://github.com/rm-dr/daisy
Added support for scientific notation
parent
52a508cd98
commit
85a669e126
|
@ -176,6 +176,13 @@ fn main() -> Result<(), std::io::Error> {
|
||||||
// Tests to add:
|
// Tests to add:
|
||||||
// 3!+1
|
// 3!+1
|
||||||
// 3!3
|
// 3!3
|
||||||
|
// 1e2
|
||||||
|
// 1e-2
|
||||||
|
// 1e0
|
||||||
|
// 1e1.2
|
||||||
|
// 1e(2)
|
||||||
|
// e2e
|
||||||
|
// 2 2e2
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
|
@ -31,6 +31,24 @@ fn push_token(g: &mut VecDeque<PreToken>, t: Option<PreToken>, stop_i: usize) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// `2e` isn't exponential notation, it's 2*e.
|
||||||
|
// If a number ends in `e`, disconnect the `e` and make it a word.
|
||||||
|
if let PreToken::PreNumber(l, s) = &t {
|
||||||
|
let last = &s[s.len()-1..];
|
||||||
|
if last == "e" {
|
||||||
|
g.push_back(PreToken::PreNumber(
|
||||||
|
LineLocation { pos: l.pos, len: l.len-1 },
|
||||||
|
String::from(&s[0..s.len()-1])
|
||||||
|
));
|
||||||
|
g.push_back(PreToken::PreWord(
|
||||||
|
LineLocation { pos: l.pos + l.len - 1, len: 1 },
|
||||||
|
String::from("e")
|
||||||
|
));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let PreToken::PreWord(l, s) = &t {
|
if let PreToken::PreWord(l, s) = &t {
|
||||||
let o = Operator::from_string(s);
|
let o = Operator::from_string(s);
|
||||||
if o.is_some() {
|
if o.is_some() {
|
||||||
|
@ -68,14 +86,50 @@ pub(in crate::parser) fn tokenize(input: &String) -> VecDeque<PreToken> {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
// The minus sign can be both a Negative and an Operator.
|
// 'e' needs special treatment.
|
||||||
// Needs special treatment, always starts a new token.
|
// Can be both a word or a number.
|
||||||
|
'e' => {
|
||||||
|
match &mut t {
|
||||||
|
Some(PreToken::PreWord(_, val)) => { val.push(c); },
|
||||||
|
Some(PreToken::PreNumber(_, val)) => { val.push(c); },
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
push_token(&mut g, t, i);
|
||||||
|
t = Some(PreToken::PreWord(LineLocation{pos: i, len: 0}, String::from(c)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// The minus sign also needs special treatment.
|
||||||
|
// It can be the `neg` operator, the `minus` operator,
|
||||||
|
// or it can specify a negative exponent.
|
||||||
'-' => {
|
'-' => {
|
||||||
push_token(&mut g, t, i);
|
match &mut t {
|
||||||
t = Some(PreToken::PreOperator(
|
Some(PreToken::PreNumber(_, val)) => {
|
||||||
LineLocation{pos: i, len: 1},
|
if &val[val.len()-1..] == "e" {
|
||||||
String::from("-")
|
// If the current number ends in an `e`,
|
||||||
));
|
// this negative specifies a negative exponent
|
||||||
|
// like 2e-2 = 0.02.
|
||||||
|
val.push(c);
|
||||||
|
} else {
|
||||||
|
// Otherwise, end the number.
|
||||||
|
// We probably have a subtraction.
|
||||||
|
push_token(&mut g, t, i);
|
||||||
|
t = Some(PreToken::PreOperator(
|
||||||
|
LineLocation{pos: i, len: 1},
|
||||||
|
String::from("-")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
push_token(&mut g, t, i);
|
||||||
|
t = Some(PreToken::PreOperator(
|
||||||
|
LineLocation{pos: i, len: 1},
|
||||||
|
String::from("-")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
// Operator
|
// Operator
|
||||||
|
|
|
@ -137,20 +137,48 @@ impl Quantity {
|
||||||
|
|
||||||
pub fn new_rational_from_float_string(s: &str) -> Option<Quantity> {
|
pub fn new_rational_from_float_string(s: &str) -> Option<Quantity> {
|
||||||
|
|
||||||
let mut q = s.split(".");
|
// Scientific notation
|
||||||
let a = q.next().unwrap();
|
let mut sci = s.split("e");
|
||||||
let b = q.next();
|
let num = sci.next().unwrap();
|
||||||
|
let exp = sci.next();
|
||||||
|
|
||||||
|
let exp = if exp.is_some() {
|
||||||
|
let r = exp.unwrap().parse::<isize>();
|
||||||
|
match r {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(_) => return None
|
||||||
|
}
|
||||||
|
} else {0isize};
|
||||||
|
|
||||||
|
// Split integer and decimal parts
|
||||||
|
let mut dec = num.split(".");
|
||||||
|
let a = dec.next().unwrap();
|
||||||
|
let b = dec.next();
|
||||||
let b = if b.is_some() {b.unwrap()} else {""};
|
let b = if b.is_some() {b.unwrap()} else {""};
|
||||||
|
|
||||||
// Error conditions
|
// Error conditions
|
||||||
if {
|
if {
|
||||||
q.next().is_some() || // We should have at most one `.`
|
dec.next().is_some() || // We should have at most one `.`
|
||||||
|
sci.next().is_some() || // We should have at most one `e`
|
||||||
a.len() == 0 // We need something in the numerator
|
a.len() == 0 // We need something in the numerator
|
||||||
} { return None; }
|
} { return None; }
|
||||||
|
|
||||||
return Some(Quantity::new_rational_from_string(
|
let s: String;
|
||||||
&format!("{a}{b}/1{}", "0".repeat(b.len()))
|
if exp < 0 {
|
||||||
));
|
let exp: usize = (-exp).try_into().unwrap();
|
||||||
|
s = format!("{a}{b}/1{}", "0".repeat(b.len() + exp));
|
||||||
|
} else if exp > 0 {
|
||||||
|
let exp: usize = exp.try_into().unwrap();
|
||||||
|
s = format!(
|
||||||
|
"{a}{b}{}/1{}",
|
||||||
|
"0".repeat(exp),
|
||||||
|
"0".repeat(b.len())
|
||||||
|
);
|
||||||
|
} else { // exp == 0
|
||||||
|
s = format!("{a}{b}/1{}", "0".repeat(b.len()));
|
||||||
|
};
|
||||||
|
|
||||||
|
return Quantity::new_rational_from_string(&s);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_float(&self) -> Float {
|
pub fn to_float(&self) -> Float {
|
||||||
|
|
Loading…
Reference in New Issue