use std::collections::HashMap;
use std::str::FromStr;
use num_complex::Complex;
use crate::constants::Constants;
use crate::core::Real;
use crate::err::ParseError;
use crate::functions::{
FunctionKind,
UserFn,
};
use crate::lexer::{
Lexeme,
IMAGINARY_UNIT,
Span,
};
use crate::operators::{
OperatorKind,
BinaryOperatorKind,
DIFFERENTIAL_OPERATOR_STR,
UnaryOperatorKind,
};
pub(crate) type UserFnTable<T> = HashMap<String, UserFn<T>>;
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum Token<T: Real> {
Number {
value: Complex<T>,
span: Span,
},
Argument {
index: usize,
span: Span,
},
Operator {
kind: OperatorKind,
span: Span,
},
UnaryOperator {
kind: UnaryOperatorKind,
span: Span,
},
BinaryOperator {
kind: BinaryOperatorKind,
span: Span,
},
DiffOperator {
span: Span,
},
Function {
kind: FunctionKind,
span: Span,
},
UserFunction {
func: UserFn<T>,
span: Span,
},
LParen {
span: Span,
},
RParen {
span: Span,
},
Comma {
span: Span,
},
}
impl<T: Real> Token<T> {
fn parse_real(s: &str) -> Option<Complex<T>>
where
T: FromStr,
{
s.parse::<T>().ok().map(|v| Complex::new(v, T::zero()))
}
fn parse_imaginary(s: &str) -> Option<Complex<T>>
where
T: FromStr,
{
let num_part = s.strip_suffix(IMAGINARY_UNIT)?;
if num_part.is_empty() {
return Some(Complex::new(T::zero(), T::one()));
}
num_part.parse::<T>().ok().map(|v| Complex::new(T::zero(), v))
}
pub(crate) fn try_from(
lexeme: &Lexeme,
args: &[&str],
constants: &Constants<T>,
users: &UserFnTable<T>,
) -> Result<Self, ParseError>
where
T: FromStr,
{
let text = lexeme.text();
let span = lexeme.span();
if let Some(value) = Self::parse_real(text)
.or_else(|| Self::parse_imaginary(text))
.or_else(|| constants.get(text))
{
return Ok(Token::Number { value, span });
}
if text == DIFFERENTIAL_OPERATOR_STR {
return Ok(Token::DiffOperator { span });
}
if let Some(index) = args.iter().position(|&a| a == text) {
return Ok(Token::Argument { index, span });
}
if let Ok(kind) = OperatorKind::from_str(text)
{
return Ok(Token::Operator { kind, span });
}
if let Ok(kind) = FunctionKind::from_str(text) {
return Ok(Token::Function { kind, span });
}
if let Some(func) = users.get(text) {
return Ok(Token::UserFunction { func: func.clone(), span });
}
match text {
"(" => Ok(Token::LParen { span }),
")" => Ok(Token::RParen { span }),
"," => Ok(Token::Comma { span }),
_ => Err(ParseError::UnknownToken { str: lexeme.text().to_string(), span }),
}
}
pub(crate) fn span(&self) -> Span
{
match self {
Self::Number { span, .. }
| Self::Argument { span, .. }
| Self::Operator { span, .. }
| Self::UnaryOperator { span, .. }
| Self::BinaryOperator { span, .. }
| Self::Function { span, .. }
| Self::UserFunction { span, .. }
| Self::DiffOperator { span }
| Self::LParen { span }
| Self::RParen { span }
| Self::Comma { span }
=> *span,
}
}
}
#[cfg(test)]
mod token_tests {
use super::*;
#[test]
fn test_number_token() {
let lex = Lexeme::new("3.14", 0..4);
let constants = Constants::new();
let users = UserFnTable::new();
let args: [&str; 0] = [];
let token = Token::try_from(&lex, &args, &constants, &users).unwrap();
match token {
Token::Number { value, span: _ } => assert_eq!(value, Complex::new(3.14, 0.0)),
_ => panic!("Expected Number token"),
}
}
#[test]
fn test_imaginary_number() {
let lex = Lexeme::new("2i", 0..2);
let constants = Constants::new();
let users = UserFnTable::new();
let args: [&str; 0] = [];
let token = Token::try_from(&lex, &args, &constants, &users).unwrap();
match token {
Token::Number { value, span: _ } => assert_eq!(value, Complex::new(0.0, 2.0)),
_ => panic!("Expected Number token"),
}
let lex = Lexeme::new("i", 0..2);
let token = Token::try_from(&lex, &args, &constants, &users).unwrap();
match token {
Token::Number { value, span: _ } => assert_eq!(value, Complex::new(0.0, 1.0)),
_ => panic!("Expected Number token"),
}
}
#[test]
fn test_constant_token() {
let lex = Lexeme::new("PI", 0..2);
let constants = Constants::default();
let users = UserFnTable::new();
let args: [&str; 0] = [];
let token = Token::try_from(&lex, &args, &constants, &users).unwrap();
match token {
Token::Number { value, span: _ } => assert_eq!(value, Complex::new(std::f64::consts::PI, 0.0)),
_ => panic!("Expected Number token"),
}
}
#[test]
fn test_argument_token() {
let lex = Lexeme::new("arg0", 0..4);
let constants = Constants::<f64>::new();
let args = ["arg0"];
let users = UserFnTable::new();
let token = Token::try_from(&lex, &args, &constants, &users).unwrap();
match token {
Token::Argument { index, span: _ } => assert_eq!(index, 0),
_ => panic!("Expected Argument token"),
}
}
#[test]
fn test_operator_token() {
let lex = Lexeme::new("+", 0..1);
let constants = Constants::<f64>::new();
let args: [&str; 0] = [];
let users = UserFnTable::new();
let token = Token::try_from(&lex, &args, &constants, &users).unwrap();
match token {
Token::Operator { kind: _, span: _ } => {}, _ => panic!("Expected Operator token"),
}
}
#[test]
fn test_function_token() {
let lex = Lexeme::new("sin", 0..3);
let constants = Constants::<f64>::new();
let users = UserFnTable::new();
let args: [&str; 0] = [];
let token = Token::try_from(&lex, &args, &constants, &users).unwrap();
match token {
Token::Function { kind, span: _} => assert_eq!(kind, FunctionKind::Sin),
_ => panic!("Expected Function token"),
}
}
#[test]
fn test_parentheses_and_comma() {
let lex_l = Lexeme::new("(", 0..1);
let lex_r = Lexeme::new(")", 0..1);
let lex_c = Lexeme::new(",", 0..1);
let constants = Constants::<f64>::new();
let users = UserFnTable::new();
let args: [&str; 0] = [];
assert!(matches!(Token::try_from(&lex_l, &args, &constants, &users).unwrap(), Token::LParen { span: _ }));
assert!(matches!(Token::try_from(&lex_r, &args, &constants, &users).unwrap(), Token::RParen { span: _ }));
assert!(matches!(Token::try_from(&lex_c, &args, &constants, &users).unwrap(), Token::Comma { span: _ }));
}
#[test]
fn test_unknown_string() {
let lex = Lexeme::new("unknown", 0..7);
let constants = Constants::<f64>::new();
let users = UserFnTable::new();
let args: [&str; 0] = [];
let res = Token::try_from(&lex, &args, &constants, &users);
assert!(res.is_err());
}
}