Print result in another base when appropriate

This commit is contained in:
PaddiM8 2021-12-31 19:32:04 +01:00
parent f1881ac9b6
commit 95a5718e38
6 changed files with 138 additions and 35 deletions

View File

@ -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

View File

@ -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,
}
}

View File

@ -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,
}
}

View File

@ -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,

View File

@ -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()))

View File

@ -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())
)
}
}