mirror of
https://github.com/PaddiM8/kalker.git
synced 2024-12-13 01:50:41 +01:00
Print result in another base when appropriate
This commit is contained in:
parent
f1881ac9b6
commit
95a5718e38
@ -13,6 +13,7 @@ pub mod regular;
|
||||
pub use regular::*;
|
||||
|
||||
use crate::ast::Expr;
|
||||
use crate::radix;
|
||||
use lazy_static::lazy_static;
|
||||
use std::collections::HashMap;
|
||||
use wasm_bindgen::prelude::*;
|
||||
@ -76,7 +77,7 @@ impl ScientificNotation {
|
||||
let mut digits_and_mul = if self.digits == "1" {
|
||||
String::new()
|
||||
} else {
|
||||
format!("{}*", &self.digits)
|
||||
format!("{}×", &self.digits)
|
||||
};
|
||||
|
||||
if self.digits.len() > 1 {
|
||||
@ -107,8 +108,8 @@ impl KalkNum {
|
||||
complex_number_type: ComplexNumberType,
|
||||
) -> ScientificNotation {
|
||||
let value_string = match complex_number_type {
|
||||
ComplexNumberType::Real => self.to_string_real(),
|
||||
ComplexNumberType::Imaginary => self.to_string_imaginary(false),
|
||||
ComplexNumberType::Real => self.to_string_real(10),
|
||||
ComplexNumberType::Imaginary => self.to_string_imaginary(10, false),
|
||||
}
|
||||
.trim_start_matches("-")
|
||||
.to_string();
|
||||
@ -170,12 +171,12 @@ impl KalkNum {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_string_real(&self) -> String {
|
||||
format_number(self.to_f64())
|
||||
pub fn to_string_real(&self, radix: u8) -> String {
|
||||
radix::to_radix_pretty(self.to_f64(), radix)
|
||||
}
|
||||
|
||||
pub fn to_string_imaginary(&self, include_i: bool) -> String {
|
||||
let value = format_number(self.imaginary_to_f64());
|
||||
pub fn to_string_imaginary(&self, radix: u8, include_i: bool) -> String {
|
||||
let value = radix::to_radix_pretty(self.imaginary_to_f64(), radix);
|
||||
if include_i && value == "1" {
|
||||
String::from("i")
|
||||
} else if include_i && value == "-1" {
|
||||
@ -187,7 +188,7 @@ impl KalkNum {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_string_pretty(&self) -> String {
|
||||
fn to_string_pretty_radix(&self, radix: u8) -> String {
|
||||
if let Some(boolean_value) = self.boolean_value {
|
||||
return boolean_value.to_string();
|
||||
}
|
||||
@ -205,12 +206,16 @@ impl KalkNum {
|
||||
let sci_notation_real = self.to_scientific_notation(ComplexNumberType::Real);
|
||||
let mut adjusted_num = self.clone();
|
||||
let result_str = if (-6..8).contains(&sci_notation_real.exponent) || self.value == 0f64 {
|
||||
self.to_string_real()
|
||||
self.to_string_real(radix)
|
||||
} else if sci_notation_real.exponent <= -14 {
|
||||
adjusted_num.value = KalkNum::from(0f64).value;
|
||||
String::from("0")
|
||||
} else {
|
||||
sci_notation_real.to_string().trim().to_string()
|
||||
if radix == 10 {
|
||||
sci_notation_real.to_string().trim().to_string()
|
||||
} else {
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
let sci_notation_imaginary = self.to_scientific_notation(ComplexNumberType::Imaginary);
|
||||
@ -218,12 +223,16 @@ impl KalkNum {
|
||||
|| self.imaginary_value == 0f64
|
||||
|| self.imaginary_value == 1f64
|
||||
{
|
||||
self.to_string_imaginary(true)
|
||||
self.to_string_imaginary(radix, true)
|
||||
} else if sci_notation_imaginary.exponent <= -14 {
|
||||
adjusted_num.imaginary_value = KalkNum::from(0f64).value;
|
||||
String::from("0")
|
||||
} else {
|
||||
format!("{}", sci_notation_imaginary.to_string().trim())
|
||||
if radix == 10 {
|
||||
format!("{}", sci_notation_imaginary.to_string().trim())
|
||||
} else {
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
let mut output = result_str;
|
||||
@ -256,7 +265,7 @@ impl KalkNum {
|
||||
}
|
||||
|
||||
if let Some(estimate) = adjusted_num.estimate() {
|
||||
if estimate != output {
|
||||
if estimate != output && radix == 10 {
|
||||
output.push_str(&format!(" ≈ {}", estimate));
|
||||
}
|
||||
}
|
||||
@ -264,6 +273,17 @@ impl KalkNum {
|
||||
output
|
||||
}
|
||||
|
||||
pub fn to_string_pretty(&self) -> String {
|
||||
if let Some(other_radix) = self.other_radix {
|
||||
let with_other_radix = self.to_string_pretty_radix(other_radix);
|
||||
if with_other_radix != "" {
|
||||
return format!("{}\n{}", self.to_string_pretty_radix(10), with_other_radix);
|
||||
}
|
||||
}
|
||||
|
||||
self.to_string_pretty_radix(10)
|
||||
}
|
||||
|
||||
pub fn is_too_big(&self) -> bool {
|
||||
self.value.is_infinite()
|
||||
}
|
||||
@ -474,13 +494,13 @@ impl KalkNum {
|
||||
if let Some(value) = rounded_real {
|
||||
output.push_str(&value);
|
||||
} else if self.has_real() {
|
||||
output.push_str(&self.to_string_real());
|
||||
output.push_str(&self.to_string_real(10));
|
||||
}
|
||||
|
||||
let imaginary_value = if let Some(value) = rounded_imaginary {
|
||||
Some(value)
|
||||
} else if self.has_imaginary() {
|
||||
Some(self.to_string_imaginary(false))
|
||||
Some(self.to_string_imaginary(10, false))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -520,7 +540,9 @@ impl KalkNum {
|
||||
fn estimate_one_value(&self, complex_number_type: ComplexNumberType) -> Option<String> {
|
||||
let (value, value_string) = match complex_number_type {
|
||||
ComplexNumberType::Real => (&self.value, self.to_string()),
|
||||
ComplexNumberType::Imaginary => (&self.imaginary_value, self.to_string_imaginary(true)),
|
||||
ComplexNumberType::Imaginary => {
|
||||
(&self.imaginary_value, self.to_string_imaginary(10, true))
|
||||
}
|
||||
};
|
||||
|
||||
let fract = value.clone().fract().abs();
|
||||
@ -679,7 +701,7 @@ impl KalkNum {
|
||||
}
|
||||
}
|
||||
|
||||
fn format_number(input: f64) -> String {
|
||||
pub fn format_number(input: f64) -> String {
|
||||
let rounded = format!("{:.1$}", input, 10);
|
||||
if rounded.contains(".") {
|
||||
rounded
|
||||
|
@ -8,6 +8,7 @@ pub struct KalkNum {
|
||||
pub(crate) unit: String,
|
||||
pub(crate) imaginary_value: f64,
|
||||
pub(crate) boolean_value: Option<bool>,
|
||||
pub(crate) other_radix: Option<u8>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
@ -18,6 +19,7 @@ impl KalkNum {
|
||||
unit: unit.to_string(),
|
||||
imaginary_value: 0f64,
|
||||
boolean_value: None,
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,6 +33,7 @@ impl KalkNum {
|
||||
imaginary_value
|
||||
},
|
||||
boolean_value: None,
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,6 +43,7 @@ impl KalkNum {
|
||||
unit: String::new(),
|
||||
imaginary_value: value,
|
||||
boolean_value: None,
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,6 +53,7 @@ impl KalkNum {
|
||||
unit: String::new(),
|
||||
imaginary_value: 0f64,
|
||||
boolean_value: Some(value),
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ pub struct KalkNum {
|
||||
pub(crate) unit: String,
|
||||
pub(crate) imaginary_value: Float,
|
||||
pub(crate) boolean_value: Option<bool>,
|
||||
pub(crate) other_radix: Option<u8>,
|
||||
}
|
||||
|
||||
impl KalkNum {
|
||||
@ -22,6 +23,7 @@ impl KalkNum {
|
||||
unit: unit.to_string(),
|
||||
imaginary_value: Float::with_val(precision, 0),
|
||||
boolean_value: None,
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,6 +37,7 @@ impl KalkNum {
|
||||
imaginary_value
|
||||
},
|
||||
boolean_value: None,
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,6 +47,7 @@ impl KalkNum {
|
||||
unit: String::new(),
|
||||
imaginary_value: value,
|
||||
boolean_value: None,
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,6 +57,7 @@ impl KalkNum {
|
||||
unit: String::new(),
|
||||
imaginary_value: Float::with_val(63, 0),
|
||||
boolean_value: Some(value),
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,18 +56,23 @@ pub struct Token {
|
||||
pub struct Lexer<'a> {
|
||||
chars: Peekable<Chars<'a>>,
|
||||
index: usize,
|
||||
other_radix: Option<u8>,
|
||||
}
|
||||
|
||||
impl<'a> Lexer<'a> {
|
||||
pub fn lex(source: &str) -> Vec<Token> {
|
||||
let mut lexer = Lexer {
|
||||
pub fn new(source: &'a str) -> Self {
|
||||
Lexer {
|
||||
chars: source.chars().peekable(),
|
||||
index: 0,
|
||||
};
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lex(&mut self) -> Vec<Token> {
|
||||
let mut tokens = Vec::new();
|
||||
|
||||
loop {
|
||||
let next = lexer.next();
|
||||
let next = self.next();
|
||||
|
||||
if let TokenKind::EOF = next.kind {
|
||||
tokens.push(next);
|
||||
@ -80,6 +85,10 @@ impl<'a> Lexer<'a> {
|
||||
tokens
|
||||
}
|
||||
|
||||
pub fn get_other_radix(&self) -> Option<u8> {
|
||||
self.other_radix
|
||||
}
|
||||
|
||||
fn next(&mut self) -> Token {
|
||||
let eof = build(TokenKind::EOF, "", (self.index, self.index));
|
||||
let mut c = if let Some(c) = self.peek() {
|
||||
@ -179,7 +188,7 @@ impl<'a> Lexer<'a> {
|
||||
let mut end = start;
|
||||
let mut value = String::new();
|
||||
let mut leading_zero = self.peek().unwrap_or(&'\0') == &'0';
|
||||
let mut base = 10u32;
|
||||
let mut base = 10u8;
|
||||
|
||||
loop {
|
||||
let c = if let Some(c) = self.peek() {
|
||||
@ -210,7 +219,7 @@ impl<'a> Lexer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
if !c.is_digit(base) && c != '.' && c != '_' && !c.is_whitespace()
|
||||
if !c.is_digit(base as u32) && c != '.' && c != '_' && !c.is_whitespace()
|
||||
|| c == '\n'
|
||||
|| c == '\r'
|
||||
{
|
||||
@ -231,12 +240,21 @@ impl<'a> Lexer<'a> {
|
||||
|
||||
if base_str != "" {
|
||||
base = crate::text_utils::subscript_to_digits(base_str.chars())
|
||||
.parse::<u32>()
|
||||
.parse::<u8>()
|
||||
.unwrap_or(10);
|
||||
}
|
||||
|
||||
if base != 10 {
|
||||
value.push_str(&format!("_{}", base));
|
||||
if let Some(other_radix) = self.other_radix {
|
||||
// Don't bother keeping track of radixes
|
||||
// if several different ones are used
|
||||
if other_radix != base {
|
||||
self.other_radix = None;
|
||||
}
|
||||
} else {
|
||||
self.other_radix = Some(base);
|
||||
}
|
||||
}
|
||||
|
||||
build(TokenKind::Literal, &value, (start, end))
|
||||
@ -358,7 +376,7 @@ mod tests {
|
||||
#[test]
|
||||
#[wasm_bindgen_test]
|
||||
fn test_token_kinds() {
|
||||
let tokens = Lexer::lex("+-*/%^()|=!,");
|
||||
let tokens = Lexer::new("+-*/%^()|=!,").lex();
|
||||
let expected = vec![
|
||||
TokenKind::Plus,
|
||||
TokenKind::Minus,
|
||||
@ -381,7 +399,7 @@ mod tests {
|
||||
#[test]
|
||||
#[wasm_bindgen_test]
|
||||
fn test_brackets() {
|
||||
let tokens = Lexer::lex("[1 < 2]");
|
||||
let tokens = Lexer::new("[1 < 2]").lex();
|
||||
let expected = vec![
|
||||
TokenKind::OpenBracket,
|
||||
TokenKind::Literal,
|
||||
@ -401,7 +419,7 @@ mod tests {
|
||||
let test_cases = vec![" ", " ", "test ", " test "];
|
||||
|
||||
for input in test_cases {
|
||||
let tokens = Lexer::lex(input);
|
||||
let tokens = Lexer::new(input).lex();
|
||||
|
||||
if regex::Regex::new(r"^\s*$").unwrap().is_match(input) {
|
||||
let expected = vec![TokenKind::EOF];
|
||||
@ -417,7 +435,7 @@ mod tests {
|
||||
#[test_case("24")]
|
||||
#[test_case("56.4")]
|
||||
fn test_number_literal(input: &str) {
|
||||
let tokens = Lexer::lex(input);
|
||||
let tokens = Lexer::new(input).lex();
|
||||
let expected = vec![TokenKind::Literal, TokenKind::EOF];
|
||||
|
||||
assert_eq!(&tokens[0].value, input);
|
||||
@ -427,7 +445,7 @@ mod tests {
|
||||
#[test_case("x")]
|
||||
#[test_case("xy")]
|
||||
fn test_identifier(input: &str) {
|
||||
let tokens = Lexer::lex(input);
|
||||
let tokens = Lexer::new(input).lex();
|
||||
let expected = vec![TokenKind::Identifier, TokenKind::EOF];
|
||||
|
||||
assert_eq!(&tokens[0].value, input);
|
||||
@ -436,7 +454,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_function_call() {
|
||||
let tokens = Lexer::lex("f(x)");
|
||||
let tokens = Lexer::new("f(x)").lex();
|
||||
let expected = vec![
|
||||
TokenKind::Identifier,
|
||||
TokenKind::OpenParenthesis,
|
||||
|
@ -33,6 +33,7 @@ pub struct Context {
|
||||
is_in_integral: bool,
|
||||
current_function: Option<String>,
|
||||
current_function_parameters: Option<Vec<String>>,
|
||||
other_radix: Option<u8>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
@ -53,6 +54,7 @@ impl Context {
|
||||
is_in_integral: false,
|
||||
current_function: None,
|
||||
current_function_parameters: None,
|
||||
other_radix: None,
|
||||
};
|
||||
|
||||
parse(&mut context, crate::prelude::INIT).unwrap();
|
||||
@ -176,17 +178,25 @@ pub fn eval(
|
||||
None
|
||||
},
|
||||
);
|
||||
interpreter.interpret(statements)
|
||||
let result = interpreter.interpret(statements);
|
||||
if let Ok(Some(mut num)) = result {
|
||||
num.other_radix = context.other_radix;
|
||||
Ok(Some(num))
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse expressions/declarations and return a syntax tree.
|
||||
///
|
||||
/// `None` will be returned if the last statement is a declaration.
|
||||
pub fn parse(context: &mut Context, input: &str) -> Result<Vec<Stmt>, CalcError> {
|
||||
context.tokens = Lexer::lex(input);
|
||||
let mut lexer = Lexer::new(input);
|
||||
context.tokens = lexer.lex();
|
||||
context.pos = 0;
|
||||
context.parsing_unit_decl = false;
|
||||
context.unit_decl_base_unit = None;
|
||||
context.other_radix = lexer.get_other_radix();
|
||||
|
||||
let mut statements: Vec<Stmt> = Vec::new();
|
||||
while !is_at_end(context) {
|
||||
@ -856,7 +866,7 @@ fn is_at_end(context: &Context) -> bool {
|
||||
|
||||
fn string_to_num(value: &str) -> Result<f64, CalcError> {
|
||||
let base = get_base(value)?;
|
||||
if let Some(result) = crate::radix::parse_float_radix(value.replace(" ", ""), base) {
|
||||
if let Some(result) = crate::radix::parse_float_radix(&value.replace(" ", ""), base) {
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(CalcError::InvalidNumberLiteral(value.into()))
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub fn parse_float_radix(value: String, radix: u8) -> Option<f64> {
|
||||
pub fn parse_float_radix(value: &str, radix: u8) -> Option<f64> {
|
||||
if radix == 10 {
|
||||
return if let Ok(result) = value.parse::<f64>() {
|
||||
Some(result)
|
||||
@ -9,7 +9,7 @@ pub fn parse_float_radix(value: String, radix: u8) -> Option<f64> {
|
||||
|
||||
let mut sum = 0f64;
|
||||
let length = value.find('_').unwrap_or(value.len());
|
||||
let mut i = (value.find('.').unwrap_or(length) - 1) as i32;
|
||||
let mut i = (value.find('.').unwrap_or(length) as i32) - 1;
|
||||
for c in value.chars() {
|
||||
if c == '_' {
|
||||
break;
|
||||
@ -26,3 +26,46 @@ pub fn parse_float_radix(value: String, radix: u8) -> Option<f64> {
|
||||
|
||||
return Some(sum);
|
||||
}
|
||||
|
||||
const DIGITS: &'static str = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
pub fn int_to_radix(value: i64, radix: u8) -> String {
|
||||
let mut num = value.abs();
|
||||
let mut result_str = String::new();
|
||||
while num > 0 {
|
||||
let digit_index = (num % radix as i64) as usize;
|
||||
result_str.insert(0, DIGITS.as_bytes()[digit_index] as char);
|
||||
num /= radix as i64;
|
||||
}
|
||||
|
||||
if result_str == "" {
|
||||
return String::from("0");
|
||||
}
|
||||
|
||||
let sign = if value.is_positive() { "" } else { "-" };
|
||||
format!("{}{}", sign, result_str)
|
||||
}
|
||||
|
||||
pub fn float_to_radix(value: f64, radix: u8) -> String {
|
||||
let mut result = int_to_radix(value.floor() as i64, radix);
|
||||
let fract = value.fract();
|
||||
if fract != 0f64 {
|
||||
result.push('.');
|
||||
let precision = 10;
|
||||
let fract_digits = (fract * (radix as i64).pow(precision) as f64) as i64;
|
||||
result.push_str(&int_to_radix(fract_digits, radix).trim_end_matches('0'))
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn to_radix_pretty(value: f64, radix: u8) -> String {
|
||||
if radix == 10 {
|
||||
crate::kalk_num::format_number(value)
|
||||
} else {
|
||||
format!(
|
||||
"{}{}",
|
||||
float_to_radix(value, radix),
|
||||
crate::text_utils::digits_to_subscript(radix.to_string().chars())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user