mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-01-05 21:18:59 +01:00
Add E-notation
This commit is contained in:
parent
c1d8ceb646
commit
22ccb03ec9
@ -66,6 +66,8 @@ pub struct Lexer<'a> {
|
||||
chars: Peekable<Chars<'a>>,
|
||||
index: usize,
|
||||
other_radix: Option<u8>,
|
||||
buffer: Option<char>,
|
||||
has_backtracked: bool,
|
||||
}
|
||||
|
||||
impl<'a> Lexer<'a> {
|
||||
@ -74,6 +76,8 @@ impl<'a> Lexer<'a> {
|
||||
chars: source.chars().peekable(),
|
||||
index: 0,
|
||||
other_radix: None,
|
||||
buffer: None,
|
||||
has_backtracked: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,7 +109,7 @@ impl<'a> Lexer<'a> {
|
||||
fn next(&mut self) -> Token {
|
||||
let eof = build(TokenKind::Eof, "", (self.index, self.index));
|
||||
let mut c = if let Some(c) = self.peek() {
|
||||
*c
|
||||
c
|
||||
} else {
|
||||
return eof;
|
||||
};
|
||||
@ -116,7 +120,7 @@ impl<'a> Lexer<'a> {
|
||||
}
|
||||
|
||||
c = if let Some(c) = self.peek() {
|
||||
*c
|
||||
c
|
||||
} else {
|
||||
return eof;
|
||||
}
|
||||
@ -126,7 +130,7 @@ impl<'a> Lexer<'a> {
|
||||
return self.next_number_literal();
|
||||
}
|
||||
|
||||
if is_valid_identifier(Some(&c)) {
|
||||
if is_valid_identifier(Some(c)) {
|
||||
return self.next_identifier();
|
||||
}
|
||||
|
||||
@ -221,10 +225,12 @@ impl<'a> Lexer<'a> {
|
||||
let mut start = self.index;
|
||||
let mut end = start;
|
||||
let mut value = String::new();
|
||||
let mut leading_zero = self.peek().unwrap_or(&'\0') == &'0';
|
||||
let mut leading_zero = self.peek().unwrap_or('\0') == '0';
|
||||
let mut base = 10u8;
|
||||
let mut is_e_notation = false;
|
||||
|
||||
while let Some(c) = self.peek() {
|
||||
let c = c.clone();
|
||||
// If at the second character and
|
||||
// the first character is a zero,
|
||||
// allow a letter
|
||||
@ -247,22 +253,46 @@ impl<'a> Lexer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
if !c.is_digit(base as u32) && *c != '.' && *c != '_' && !c.is_whitespace()
|
||||
|| *c == '\n'
|
||||
|| *c == '\r'
|
||||
if is_e_notation && c == 'E' {
|
||||
break;
|
||||
}
|
||||
|
||||
if end != start && c == 'E' {
|
||||
is_e_notation = true;
|
||||
end += 1;
|
||||
value.push(c);
|
||||
self.advance();
|
||||
|
||||
if let Some('-') = self.peek() {
|
||||
end += 1;
|
||||
value.push('-');
|
||||
self.advance();
|
||||
} else if !self.peek().unwrap_or('\0').is_ascii_digit() {
|
||||
end -= 1;
|
||||
value.pop();
|
||||
self.backtrack();
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if !c.is_digit(base as u32) && c != '.' && c != '_' && !c.is_whitespace()
|
||||
|| c == '\n'
|
||||
|| c == '\r'
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
end += 1;
|
||||
value.push(*c);
|
||||
value.push(c);
|
||||
self.advance();
|
||||
}
|
||||
|
||||
// Subscript unicode symbols after the literal, eg. 11₂
|
||||
let mut base_str = String::new();
|
||||
while crate::text_utils::is_subscript(self.peek().unwrap_or(&'\0')) {
|
||||
base_str.push(*self.peek().unwrap());
|
||||
while crate::text_utils::is_subscript(&self.peek().unwrap_or('\0')) {
|
||||
base_str.push(self.peek().unwrap());
|
||||
self.advance();
|
||||
}
|
||||
|
||||
@ -295,7 +325,7 @@ impl<'a> Lexer<'a> {
|
||||
let mut subscript = String::new();
|
||||
|
||||
while is_valid_identifier(self.peek()) {
|
||||
let c = *self.peek().unwrap();
|
||||
let c = self.peek().unwrap();
|
||||
|
||||
// If the current character is an underscore, allow a number next.
|
||||
// This is to allow the notation like the following: x_1
|
||||
@ -379,14 +409,30 @@ impl<'a> Lexer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn peek(&mut self) -> Option<&char> {
|
||||
self.chars.peek()
|
||||
fn peek(&mut self) -> Option<char> {
|
||||
if self.has_backtracked {
|
||||
self.buffer
|
||||
} else {
|
||||
self.chars.peek().copied()
|
||||
}
|
||||
}
|
||||
|
||||
fn advance(&mut self) -> Option<char> {
|
||||
self.index += 1;
|
||||
if self.has_backtracked {
|
||||
self.has_backtracked = false;
|
||||
|
||||
return self.buffer;
|
||||
}
|
||||
|
||||
self.buffer = self.peek();
|
||||
self.chars.next()
|
||||
}
|
||||
|
||||
fn backtrack(&mut self) {
|
||||
self.has_backtracked = true;
|
||||
self.index -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn build(kind: TokenKind, value: &str, span: (usize, usize)) -> Token {
|
||||
@ -397,14 +443,14 @@ fn build(kind: TokenKind, value: &str, span: (usize, usize)) -> Token {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_valid_identifier(c: Option<&char>) -> bool {
|
||||
fn is_valid_identifier(c: Option<char>) -> bool {
|
||||
if let Some(c) = c {
|
||||
match c {
|
||||
'+' | '-' | '/' | '*' | '%' | '^' | '!' | '(' | ')' | '=' | '.' | ',' | ';' | '|'
|
||||
| '⌊' | '⌋' | '⌈' | '⌉' | '[' | ']' | '{' | '}' | 'π' | '√' | 'τ' | 'ϕ' | 'Γ' | '<'
|
||||
| '>' | '≠' | '≥' | '≤' | '×' | '÷' | '⋅' | '⟦' | '⟧' | '∧' | '∨' | '¬' | ':' | 'ᵀ'
|
||||
| '\n' => false,
|
||||
_ => !c.is_ascii_digit() || is_superscript(c) || is_subscript(c),
|
||||
_ => !c.is_ascii_digit() || is_superscript(&c) || is_subscript(&c),
|
||||
}
|
||||
} else {
|
||||
false
|
||||
|
@ -528,7 +528,7 @@ fn parse_primary(context: &mut Context) -> Result<Expr, KalkError> {
|
||||
TokenKind::OpenParenthesis | TokenKind::OpenBracket => parse_vector(context)?,
|
||||
TokenKind::Pipe | TokenKind::OpenCeil | TokenKind::OpenFloor => parse_group_fn(context)?,
|
||||
TokenKind::Identifier => parse_identifier(context)?,
|
||||
TokenKind::Literal => Expr::Literal(crate::float!(string_to_num(&advance(context).value)?)),
|
||||
TokenKind::Literal => Expr::Literal(string_to_num(&advance(context).value)?),
|
||||
TokenKind::True => {
|
||||
advance(context);
|
||||
Expr::Boolean(true)
|
||||
@ -770,10 +770,39 @@ fn skip_newlines(context: &mut Context) {
|
||||
}
|
||||
}
|
||||
|
||||
fn string_to_num(value: &str) -> Result<f64, KalkError> {
|
||||
#[cfg(feature = "rug")]
|
||||
fn string_to_num(value: &str) -> Result<rug::Float, KalkError> {
|
||||
use rug::ops::Pow;
|
||||
|
||||
if value.contains('E') {
|
||||
let parts = value.split('E').collect::<Vec<_>>();
|
||||
let left = crate::float!(string_to_num(parts[0])?);
|
||||
let right = crate::float!(string_to_num(parts[1])?);
|
||||
|
||||
return Ok(left * 10.pow(right));
|
||||
}
|
||||
|
||||
let base = get_base(value)?;
|
||||
if let Some(result) = crate::radix::parse_float_radix(&value.replace(' ', ""), base) {
|
||||
Ok(result)
|
||||
Ok(crate::float!(result))
|
||||
} else {
|
||||
Err(KalkError::InvalidNumberLiteral(value.into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "rug"))]
|
||||
fn string_to_num(value: &str) -> Result<f64, KalkError> {
|
||||
if value.contains('E') {
|
||||
let parts = value.split('E').collect::<Vec<_>>();
|
||||
let left = crate::float!(string_to_num(parts[0])?);
|
||||
let right = crate::float!(string_to_num(parts[1])?);
|
||||
|
||||
return Ok(left * 10_f64.powf(right));
|
||||
}
|
||||
|
||||
let base = get_base(value)?;
|
||||
if let Some(result) = crate::radix::parse_float_radix(&value.replace(' ', ""), base) {
|
||||
Ok(crate::float!(result))
|
||||
} else {
|
||||
Err(KalkError::InvalidNumberLiteral(value.into()))
|
||||
}
|
||||
|
@ -5,3 +5,11 @@ g(x) = x^x
|
||||
|
||||
2f(f(x) + y) * 2 = 13600 and
|
||||
g(2) = 4
|
||||
|
||||
E = 3
|
||||
|
||||
1E-2 = 0.01 and
|
||||
1.23E2 = 123 and
|
||||
1E1 = 10 and
|
||||
2E = 6 and
|
||||
0E0 = 0
|
Loading…
Reference in New Issue
Block a user