support floating point numbers in math mode
This commit is contained in:
parent
75fc04d8b5
commit
19b2379ff0
1 changed files with 37 additions and 10 deletions
|
@ -63,7 +63,8 @@ impl<T: Clone> ItemProvider<T> for MathProvider<T> {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
enum Token {
|
enum Token {
|
||||||
Num(i64),
|
Int(i64),
|
||||||
|
Float(f64),
|
||||||
Op(char),
|
Op(char),
|
||||||
ShiftLeft,
|
ShiftLeft,
|
||||||
ShiftRight,
|
ShiftRight,
|
||||||
|
@ -91,12 +92,16 @@ fn normalize_bases(expr: &str) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_implicit_multiplication(tokens: &mut VecDeque<Token>, last_token: Option<&Token>) {
|
fn insert_implicit_multiplication(tokens: &mut VecDeque<Token>, last_token: Option<&Token>) {
|
||||||
if matches!(last_token, Some(Token::Num(_) | Token::Op(')'))) {
|
if matches!(
|
||||||
|
last_token,
|
||||||
|
Some(Token::Int(_) | Token::Float(_) | Token::Op(')'))
|
||||||
|
) {
|
||||||
tokens.push_back(Token::Op('*'));
|
tokens.push_back(Token::Op('*'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tokenize a normalized expression string into tokens
|
/// Tokenize a normalized expression string into tokens
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
fn tokenize(expr: &str) -> Result<VecDeque<Token>, String> {
|
fn tokenize(expr: &str) -> Result<VecDeque<Token>, String> {
|
||||||
let mut tokens = VecDeque::new();
|
let mut tokens = VecDeque::new();
|
||||||
let chars: Vec<char> = expr.chars().collect();
|
let chars: Vec<char> = expr.chars().collect();
|
||||||
|
@ -139,7 +144,7 @@ fn tokenize(expr: &str) -> Result<VecDeque<Token>, String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Single-character operators, parentheses, or digits
|
// Single-character operators, parentheses, or digits/float
|
||||||
match c {
|
match c {
|
||||||
'+' | '-' | '*' | '/' | '&' | '|' | '^' => {
|
'+' | '-' | '*' | '/' | '&' | '|' | '^' => {
|
||||||
let token = Token::Op(c);
|
let token = Token::Op(c);
|
||||||
|
@ -160,7 +165,7 @@ fn tokenize(expr: &str) -> Result<VecDeque<Token>, String> {
|
||||||
last_token = Some(token);
|
last_token = Some(token);
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
'0'..='9' => {
|
'0'..='9' | '.' => {
|
||||||
// Only insert implicit multiplication if the last token is ')' and the last token in tokens is not already an operator (except ')')
|
// Only insert implicit multiplication if the last token is ')' and the last token in tokens is not already an operator (except ')')
|
||||||
if let Some(Token::Op(')')) = last_token {
|
if let Some(Token::Op(')')) = last_token {
|
||||||
if let Some(Token::Op(op)) = tokens.back() {
|
if let Some(Token::Op(op)) = tokens.back() {
|
||||||
|
@ -172,14 +177,35 @@ fn tokenize(expr: &str) -> Result<VecDeque<Token>, String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let start = i;
|
let start = i;
|
||||||
while i < chars.len() && chars[i].is_ascii_digit() {
|
let mut has_dot = c == '.';
|
||||||
|
if c == '.' && (i + 1 >= chars.len() || !chars[i + 1].is_ascii_digit()) {
|
||||||
|
return Err("Invalid float literal".to_owned());
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
while i < chars.len()
|
||||||
|
&& (chars[i].is_ascii_digit() || (!has_dot && chars[i] == '.'))
|
||||||
|
{
|
||||||
|
if chars[i] == '.' {
|
||||||
|
has_dot = true;
|
||||||
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
let num_str: String = chars[start..i].iter().collect();
|
let num_str: String = chars[start..i].iter().collect();
|
||||||
let n = num_str.parse::<i64>().unwrap();
|
if has_dot {
|
||||||
let token = Token::Num(n);
|
let n = num_str
|
||||||
tokens.push_back(token);
|
.parse::<f64>()
|
||||||
last_token = Some(token);
|
.map_err(|_| "Invalid float literal".to_owned())?;
|
||||||
|
let token = Token::Float(n);
|
||||||
|
tokens.push_back(token);
|
||||||
|
last_token = Some(token);
|
||||||
|
} else {
|
||||||
|
let n = num_str
|
||||||
|
.parse::<i64>()
|
||||||
|
.map_err(|_| "Invalid integer literal".to_owned())?;
|
||||||
|
let token = Token::Int(n);
|
||||||
|
tokens.push_back(token);
|
||||||
|
last_token = Some(token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => return Err("Invalid character in expression".to_owned()),
|
_ => return Err("Invalid character in expression".to_owned()),
|
||||||
}
|
}
|
||||||
|
@ -242,7 +268,8 @@ fn eval_expr(tokens: &mut VecDeque<Token>) -> Result<Value, String> {
|
||||||
|
|
||||||
while let Some(token) = tokens.pop_front() {
|
while let Some(token) = tokens.pop_front() {
|
||||||
match token {
|
match token {
|
||||||
Token::Num(n) => values.push(Value::Int(n)),
|
Token::Int(n) => values.push(Value::Int(n)),
|
||||||
|
Token::Float(f) => values.push(Value::Float(f)),
|
||||||
Token::Op('(') => {
|
Token::Op('(') => {
|
||||||
ops.push(Token::Op('('));
|
ops.push(Token::Op('('));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue