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::*; pub use regular::*;
use crate::ast::Expr; use crate::ast::Expr;
use crate::radix;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::collections::HashMap; use std::collections::HashMap;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
@ -76,7 +77,7 @@ impl ScientificNotation {
let mut digits_and_mul = if self.digits == "1" { let mut digits_and_mul = if self.digits == "1" {
String::new() String::new()
} else { } else {
format!("{}*", &self.digits) format!("{}×", &self.digits)
}; };
if self.digits.len() > 1 { if self.digits.len() > 1 {
@ -107,8 +108,8 @@ impl KalkNum {
complex_number_type: ComplexNumberType, complex_number_type: ComplexNumberType,
) -> ScientificNotation { ) -> ScientificNotation {
let value_string = match complex_number_type { let value_string = match complex_number_type {
ComplexNumberType::Real => self.to_string_real(), ComplexNumberType::Real => self.to_string_real(10),
ComplexNumberType::Imaginary => self.to_string_imaginary(false), ComplexNumberType::Imaginary => self.to_string_imaginary(10, false),
} }
.trim_start_matches("-") .trim_start_matches("-")
.to_string(); .to_string();
@ -170,12 +171,12 @@ impl KalkNum {
} }
} }
pub fn to_string_real(&self) -> String { pub fn to_string_real(&self, radix: u8) -> String {
format_number(self.to_f64()) radix::to_radix_pretty(self.to_f64(), radix)
} }
pub fn to_string_imaginary(&self, include_i: bool) -> String { pub fn to_string_imaginary(&self, radix: u8, include_i: bool) -> String {
let value = format_number(self.imaginary_to_f64()); let value = radix::to_radix_pretty(self.imaginary_to_f64(), radix);
if include_i && value == "1" { if include_i && value == "1" {
String::from("i") String::from("i")
} else if include_i && value == "-1" { } 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 { if let Some(boolean_value) = self.boolean_value {
return boolean_value.to_string(); return boolean_value.to_string();
} }
@ -205,12 +206,16 @@ impl KalkNum {
let sci_notation_real = self.to_scientific_notation(ComplexNumberType::Real); let sci_notation_real = self.to_scientific_notation(ComplexNumberType::Real);
let mut adjusted_num = self.clone(); let mut adjusted_num = self.clone();
let result_str = if (-6..8).contains(&sci_notation_real.exponent) || self.value == 0f64 { 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 { } else if sci_notation_real.exponent <= -14 {
adjusted_num.value = KalkNum::from(0f64).value; adjusted_num.value = KalkNum::from(0f64).value;
String::from("0") String::from("0")
} else { } 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); let sci_notation_imaginary = self.to_scientific_notation(ComplexNumberType::Imaginary);
@ -218,12 +223,16 @@ impl KalkNum {
|| self.imaginary_value == 0f64 || self.imaginary_value == 0f64
|| self.imaginary_value == 1f64 || self.imaginary_value == 1f64
{ {
self.to_string_imaginary(true) self.to_string_imaginary(radix, true)
} else if sci_notation_imaginary.exponent <= -14 { } else if sci_notation_imaginary.exponent <= -14 {
adjusted_num.imaginary_value = KalkNum::from(0f64).value; adjusted_num.imaginary_value = KalkNum::from(0f64).value;
String::from("0") String::from("0")
} else { } 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; let mut output = result_str;
@ -256,7 +265,7 @@ impl KalkNum {
} }
if let Some(estimate) = adjusted_num.estimate() { if let Some(estimate) = adjusted_num.estimate() {
if estimate != output { if estimate != output && radix == 10 {
output.push_str(&format!("{}", estimate)); output.push_str(&format!("{}", estimate));
} }
} }
@ -264,6 +273,17 @@ impl KalkNum {
output 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 { pub fn is_too_big(&self) -> bool {
self.value.is_infinite() self.value.is_infinite()
} }
@ -474,13 +494,13 @@ impl KalkNum {
if let Some(value) = rounded_real { if let Some(value) = rounded_real {
output.push_str(&value); output.push_str(&value);
} else if self.has_real() { } 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 { let imaginary_value = if let Some(value) = rounded_imaginary {
Some(value) Some(value)
} else if self.has_imaginary() { } else if self.has_imaginary() {
Some(self.to_string_imaginary(false)) Some(self.to_string_imaginary(10, false))
} else { } else {
None None
}; };
@ -520,7 +540,9 @@ impl KalkNum {
fn estimate_one_value(&self, complex_number_type: ComplexNumberType) -> Option<String> { fn estimate_one_value(&self, complex_number_type: ComplexNumberType) -> Option<String> {
let (value, value_string) = match complex_number_type { let (value, value_string) = match complex_number_type {
ComplexNumberType::Real => (&self.value, self.to_string()), 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(); 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); let rounded = format!("{:.1$}", input, 10);
if rounded.contains(".") { if rounded.contains(".") {
rounded rounded

View File

@ -8,6 +8,7 @@ pub struct KalkNum {
pub(crate) unit: String, pub(crate) unit: String,
pub(crate) imaginary_value: f64, pub(crate) imaginary_value: f64,
pub(crate) boolean_value: Option<bool>, pub(crate) boolean_value: Option<bool>,
pub(crate) other_radix: Option<u8>,
} }
#[wasm_bindgen] #[wasm_bindgen]
@ -18,6 +19,7 @@ impl KalkNum {
unit: unit.to_string(), unit: unit.to_string(),
imaginary_value: 0f64, imaginary_value: 0f64,
boolean_value: None, boolean_value: None,
other_radix: None,
} }
} }
@ -31,6 +33,7 @@ impl KalkNum {
imaginary_value imaginary_value
}, },
boolean_value: None, boolean_value: None,
other_radix: None,
} }
} }
@ -40,6 +43,7 @@ impl KalkNum {
unit: String::new(), unit: String::new(),
imaginary_value: value, imaginary_value: value,
boolean_value: None, boolean_value: None,
other_radix: None,
} }
} }
@ -49,6 +53,7 @@ impl KalkNum {
unit: String::new(), unit: String::new(),
imaginary_value: 0f64, imaginary_value: 0f64,
boolean_value: Some(value), boolean_value: Some(value),
other_radix: None,
} }
} }

View File

@ -12,6 +12,7 @@ pub struct KalkNum {
pub(crate) unit: String, pub(crate) unit: String,
pub(crate) imaginary_value: Float, pub(crate) imaginary_value: Float,
pub(crate) boolean_value: Option<bool>, pub(crate) boolean_value: Option<bool>,
pub(crate) other_radix: Option<u8>,
} }
impl KalkNum { impl KalkNum {
@ -22,6 +23,7 @@ impl KalkNum {
unit: unit.to_string(), unit: unit.to_string(),
imaginary_value: Float::with_val(precision, 0), imaginary_value: Float::with_val(precision, 0),
boolean_value: None, boolean_value: None,
other_radix: None,
} }
} }
@ -35,6 +37,7 @@ impl KalkNum {
imaginary_value imaginary_value
}, },
boolean_value: None, boolean_value: None,
other_radix: None,
} }
} }
@ -44,6 +47,7 @@ impl KalkNum {
unit: String::new(), unit: String::new(),
imaginary_value: value, imaginary_value: value,
boolean_value: None, boolean_value: None,
other_radix: None,
} }
} }
@ -53,6 +57,7 @@ impl KalkNum {
unit: String::new(), unit: String::new(),
imaginary_value: Float::with_val(63, 0), imaginary_value: Float::with_val(63, 0),
boolean_value: Some(value), boolean_value: Some(value),
other_radix: None,
} }
} }

View File

@ -56,18 +56,23 @@ pub struct Token {
pub struct Lexer<'a> { pub struct Lexer<'a> {
chars: Peekable<Chars<'a>>, chars: Peekable<Chars<'a>>,
index: usize, index: usize,
other_radix: Option<u8>,
} }
impl<'a> Lexer<'a> { impl<'a> Lexer<'a> {
pub fn lex(source: &str) -> Vec<Token> { pub fn new(source: &'a str) -> Self {
let mut lexer = Lexer { Lexer {
chars: source.chars().peekable(), chars: source.chars().peekable(),
index: 0, index: 0,
}; other_radix: None,
}
}
pub fn lex(&mut self) -> Vec<Token> {
let mut tokens = Vec::new(); let mut tokens = Vec::new();
loop { loop {
let next = lexer.next(); let next = self.next();
if let TokenKind::EOF = next.kind { if let TokenKind::EOF = next.kind {
tokens.push(next); tokens.push(next);
@ -80,6 +85,10 @@ impl<'a> Lexer<'a> {
tokens tokens
} }
pub fn get_other_radix(&self) -> Option<u8> {
self.other_radix
}
fn next(&mut self) -> Token { fn next(&mut self) -> Token {
let eof = build(TokenKind::EOF, "", (self.index, self.index)); let eof = build(TokenKind::EOF, "", (self.index, self.index));
let mut c = if let Some(c) = self.peek() { let mut c = if let Some(c) = self.peek() {
@ -179,7 +188,7 @@ impl<'a> Lexer<'a> {
let mut end = start; let mut end = start;
let mut value = String::new(); 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 = 10u32; let mut base = 10u8;
loop { loop {
let c = if let Some(c) = self.peek() { 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 == '\n'
|| c == '\r' || c == '\r'
{ {
@ -231,12 +240,21 @@ impl<'a> Lexer<'a> {
if base_str != "" { if base_str != "" {
base = crate::text_utils::subscript_to_digits(base_str.chars()) base = crate::text_utils::subscript_to_digits(base_str.chars())
.parse::<u32>() .parse::<u8>()
.unwrap_or(10); .unwrap_or(10);
} }
if base != 10 { if base != 10 {
value.push_str(&format!("_{}", base)); 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)) build(TokenKind::Literal, &value, (start, end))
@ -358,7 +376,7 @@ mod tests {
#[test] #[test]
#[wasm_bindgen_test] #[wasm_bindgen_test]
fn test_token_kinds() { fn test_token_kinds() {
let tokens = Lexer::lex("+-*/%^()|=!,"); let tokens = Lexer::new("+-*/%^()|=!,").lex();
let expected = vec![ let expected = vec![
TokenKind::Plus, TokenKind::Plus,
TokenKind::Minus, TokenKind::Minus,
@ -381,7 +399,7 @@ mod tests {
#[test] #[test]
#[wasm_bindgen_test] #[wasm_bindgen_test]
fn test_brackets() { fn test_brackets() {
let tokens = Lexer::lex("[1 < 2]"); let tokens = Lexer::new("[1 < 2]").lex();
let expected = vec![ let expected = vec![
TokenKind::OpenBracket, TokenKind::OpenBracket,
TokenKind::Literal, TokenKind::Literal,
@ -401,7 +419,7 @@ mod tests {
let test_cases = vec![" ", " ", "test ", " test "]; let test_cases = vec![" ", " ", "test ", " test "];
for input in test_cases { 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) { if regex::Regex::new(r"^\s*$").unwrap().is_match(input) {
let expected = vec![TokenKind::EOF]; let expected = vec![TokenKind::EOF];
@ -417,7 +435,7 @@ mod tests {
#[test_case("24")] #[test_case("24")]
#[test_case("56.4")] #[test_case("56.4")]
fn test_number_literal(input: &str) { fn test_number_literal(input: &str) {
let tokens = Lexer::lex(input); let tokens = Lexer::new(input).lex();
let expected = vec![TokenKind::Literal, TokenKind::EOF]; let expected = vec![TokenKind::Literal, TokenKind::EOF];
assert_eq!(&tokens[0].value, input); assert_eq!(&tokens[0].value, input);
@ -427,7 +445,7 @@ mod tests {
#[test_case("x")] #[test_case("x")]
#[test_case("xy")] #[test_case("xy")]
fn test_identifier(input: &str) { fn test_identifier(input: &str) {
let tokens = Lexer::lex(input); let tokens = Lexer::new(input).lex();
let expected = vec![TokenKind::Identifier, TokenKind::EOF]; let expected = vec![TokenKind::Identifier, TokenKind::EOF];
assert_eq!(&tokens[0].value, input); assert_eq!(&tokens[0].value, input);
@ -436,7 +454,7 @@ mod tests {
#[test] #[test]
fn test_function_call() { fn test_function_call() {
let tokens = Lexer::lex("f(x)"); let tokens = Lexer::new("f(x)").lex();
let expected = vec![ let expected = vec![
TokenKind::Identifier, TokenKind::Identifier,
TokenKind::OpenParenthesis, TokenKind::OpenParenthesis,

View File

@ -33,6 +33,7 @@ pub struct Context {
is_in_integral: bool, is_in_integral: bool,
current_function: Option<String>, current_function: Option<String>,
current_function_parameters: Option<Vec<String>>, current_function_parameters: Option<Vec<String>>,
other_radix: Option<u8>,
} }
#[wasm_bindgen] #[wasm_bindgen]
@ -53,6 +54,7 @@ impl Context {
is_in_integral: false, is_in_integral: false,
current_function: None, current_function: None,
current_function_parameters: None, current_function_parameters: None,
other_radix: None,
}; };
parse(&mut context, crate::prelude::INIT).unwrap(); parse(&mut context, crate::prelude::INIT).unwrap();
@ -176,17 +178,25 @@ pub fn eval(
None 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. /// Parse expressions/declarations and return a syntax tree.
/// ///
/// `None` will be returned if the last statement is a declaration. /// `None` will be returned if the last statement is a declaration.
pub fn parse(context: &mut Context, input: &str) -> Result<Vec<Stmt>, CalcError> { 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.pos = 0;
context.parsing_unit_decl = false; context.parsing_unit_decl = false;
context.unit_decl_base_unit = None; context.unit_decl_base_unit = None;
context.other_radix = lexer.get_other_radix();
let mut statements: Vec<Stmt> = Vec::new(); let mut statements: Vec<Stmt> = Vec::new();
while !is_at_end(context) { 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> { fn string_to_num(value: &str) -> Result<f64, CalcError> {
let base = get_base(value)?; 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) Ok(result)
} else { } else {
Err(CalcError::InvalidNumberLiteral(value.into())) 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 { if radix == 10 {
return if let Ok(result) = value.parse::<f64>() { return if let Ok(result) = value.parse::<f64>() {
Some(result) Some(result)
@ -9,7 +9,7 @@ pub fn parse_float_radix(value: String, radix: u8) -> Option<f64> {
let mut sum = 0f64; let mut sum = 0f64;
let length = value.find('_').unwrap_or(value.len()); 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() { for c in value.chars() {
if c == '_' { if c == '_' {
break; break;
@ -26,3 +26,46 @@ pub fn parse_float_radix(value: String, radix: u8) -> Option<f64> {
return Some(sum); 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())
)
}
}