diff --git a/Cargo.lock b/Cargo.lock index d6720d3..584889c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -772,6 +772,7 @@ dependencies = [ "lazy_static", "num-complex", "num-rational", + "num-traits", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index 855d198..9144afb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,7 @@ [workspace] members = ["talc-lang", "talc-bin", "talc-std", "talc-macros"] resolver = "2" + +[profile.release-lto] +inherits = "release" +lto = "fat" diff --git a/talc-bin/Cargo.toml b/talc-bin/Cargo.toml index 38ae9c4..25d823b 100644 --- a/talc-bin/Cargo.toml +++ b/talc-bin/Cargo.toml @@ -3,6 +3,10 @@ name = "talc-bin" version = "0.1.0" edition = "2021" +[[bin]] +name = "talc" +path = "src/main.rs" + [dependencies] talc-lang = { path = "../talc-lang" } talc-std = { path = "../talc-std" } diff --git a/talc-bin/src/helper.rs b/talc-bin/src/helper.rs index 6afcff6..6c5103c 100644 --- a/talc-bin/src/helper.rs +++ b/talc-bin/src/helper.rs @@ -106,7 +106,7 @@ impl Highlighter for TalcHelper { } fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { - Cow::Owned(format!("\x1b[37m{}\x1b[0m", hint)) + Cow::Owned(format!("\x1b[37m{hint}\x1b[0m")) } fn highlight_char(&self, line: &str, _: usize, forced: bool) -> bool { diff --git a/talc-bin/src/main.rs b/talc-bin/src/main.rs index 1e53a9f..ea38fbb 100644 --- a/talc-bin/src/main.rs +++ b/talc-bin/src/main.rs @@ -19,6 +19,10 @@ struct Args { #[arg(short, long)] disasm: bool, + /// show disassembled bytecode + #[arg(short='H', long)] + histfile: Option, + /// enable or disable color #[arg(short, long, default_value="auto")] color: ColorChoice, diff --git a/talc-bin/src/repl.rs b/talc-bin/src/repl.rs index de2c6ce..063c47b 100644 --- a/talc-bin/src/repl.rs +++ b/talc-bin/src/repl.rs @@ -1,7 +1,7 @@ use std::{cell::RefCell, io::IsTerminal, process::ExitCode, rc::Rc}; use clap::ColorChoice; -use rustyline::{error::ReadlineError, history::MemHistory, ColorMode, Config, Editor}; +use rustyline::{error::ReadlineError, history::{FileHistory, History}, ColorMode, Config, Editor}; use talc_lang::{compiler::compile_repl, symbol::Symbol, value::{function::disasm_recursive, Value}, Vm}; use crate::{helper::TalcHelper, Args}; @@ -39,21 +39,30 @@ fn get_colmode(args: &Args) -> ColorMode { } } -pub fn init_rustyline(args: &Args) -> Result, ExitCode> { +pub fn init_rustyline(args: &Args) -> Result, ExitCode> { let config = Config::builder() .auto_add_history(true) .color_mode(get_colmode(args)) .check_cursor_position(true) .completion_type(rustyline::CompletionType::List) + .max_history_size(4096).unwrap() .build(); - match rustyline::Editor::with_history(config, MemHistory::default()) { + let mut hist = FileHistory::default(); + if let Some(f) = &args.histfile { + if hist.load(f).is_err() { + eprintln!("Warn: failed to load history"); + } else if hist.save(f).is_err() { + eprintln!("Warn: failed to save history"); + } + } + match rustyline::Editor::with_history(config, hist) { Ok(rl) => Ok(rl), Err(ReadlineError::Io(e)) => { - eprintln!("Error: {e}"); + eprintln!("Error creating repl: {e}"); Err(ExitCode::FAILURE) }, Err(ReadlineError::Errno(e)) => { - eprintln!("Error: {e}"); + eprintln!("Error creating repl: {e}"); Err(ExitCode::FAILURE) }, Err(_) => Err(ExitCode::SUCCESS) @@ -75,7 +84,7 @@ pub fn repl(args: &Args) -> ExitCode { interrupt.fetch_or(true, std::sync::atomic::Ordering::Relaxed); }); if let Err(e) = ctrlc_res { - eprintln!("Warn: couldn't set ctrl+c handler: {e}") + eprintln!("Warn: couldn't set ctrl+c handler: {e}"); } let prev1_sym = Symbol::get("_"); @@ -98,6 +107,9 @@ pub fn repl(args: &Args) -> ExitCode { rl.set_helper(Some(TalcHelper::new(vm.clone()))); loop { + if let Some(f) = &args.histfile { + let _ = rl.save_history(f); + } let line = rl.readline(">> "); let line = match line { Ok(line) => line, diff --git a/talc-lang/Cargo.toml b/talc-lang/Cargo.toml index aa4ea01..3e0a900 100644 --- a/talc-lang/Cargo.toml +++ b/talc-lang/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" lalrpop-util = { version = "0.20", features = ["lexer", "unicode"] } num-complex = "0.4" num-rational = { version = "0.4", default-features = false, features = [] } +num-traits = "*" thiserror = "1.0" lazy_static = "1.4" diff --git a/talc-lang/src/symbol.rs b/talc-lang/src/symbol.rs index 021c2a4..8f8d22d 100644 --- a/talc-lang/src/symbol.rs +++ b/talc-lang/src/symbol.rs @@ -43,6 +43,7 @@ static TABLE: OnceLock> = OnceLock::new(); const MAX_SYMBOL: usize = 0xff_ffff; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +#[repr(transparent)] pub struct Symbol(u32); fn get_table() -> &'static Mutex { diff --git a/talc-lang/src/value/index.rs b/talc-lang/src/value/index.rs index 3397562..835eb48 100644 --- a/talc-lang/src/value/index.rs +++ b/talc-lang/src/value/index.rs @@ -34,7 +34,7 @@ impl Value { let col = col.clone(); let func = move |vm: &mut Vm, _| { match vmcalliter!(vm; ii.clone())? { - Some(i) => Ok(col.index(i)?.to_cell()), + Some(i) => col.index(i), None => Ok(Value::from(*SYM_END_ITERATION)), } }; @@ -82,7 +82,7 @@ impl Value { } let end = tm.split_off(r.stop as usize); tm.truncate(r.start as usize); - tm.extend(vals.into_iter().chain(end)) + tm.extend(vals.into_iter().chain(end)); }, RangeType::Closed => { if r.start < 0 || r.start > tm.len() as i64 { @@ -95,7 +95,7 @@ impl Value { } let end = tm.split_off(r.stop as usize + 1); tm.truncate(r.start as usize); - tm.extend(vals.into_iter().chain(end)) + tm.extend(vals.into_iter().chain(end)); }, RangeType::Endless => { if r.start < 0 || r.start > tm.len() as i64 { @@ -103,7 +103,7 @@ impl Value { "index {} out of bounds for list of length {}", r.stop, tm.len()) } tm.truncate(r.start as usize); - tm.extend(vals) + tm.extend(vals); }, } Ok(()) diff --git a/talc-lang/src/value/ops.rs b/talc-lang/src/value/ops.rs index ebd6349..27b285a 100644 --- a/talc-lang/src/value/ops.rs +++ b/talc-lang/src/value/ops.rs @@ -7,6 +7,16 @@ use crate::{exception::{throw, Result}, symbol::{SYM_END_ITERATION, SYM_TYPE_ERR use super::{function::{FuncAttrs, NativeFunc}, range::Range, HashValue, Value}; +pub trait RatioExt { + fn to_f64(&self) -> f64; +} + +impl RatioExt for Rational64 { + fn to_f64(&self) -> f64 { + num_traits::ToPrimitive::to_f64(self).unwrap_or(f64::NAN) + } +} + impl Value { pub fn truthy(&self) -> bool { match self { @@ -27,11 +37,6 @@ impl Value { } } -#[allow(clippy::cast_precision_loss)] -fn ratio_to_f64(r: Rational64) -> f64 { - *r.numer() as f64 / *r.denom() as f64 -} - #[allow(clippy::cast_precision_loss)] pub fn promote(a: Value, b: Value) -> (Value, Value) { use Value as V; @@ -39,14 +44,14 @@ pub fn promote(a: Value, b: Value) -> (Value, Value) { (V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b), (V::Int(x), V::Float(..)) => (V::Float(*x as f64), b), (V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b), - (V::Ratio(x), V::Float(..)) => (V::Float(ratio_to_f64(*x)), b), - (V::Ratio(x), V::Complex(..)) => (V::Complex(ratio_to_f64(*x).into()), b), - (V::Float(x), V::Complex(..)) => (V::Complex((*x).into()), b), + (V::Ratio(x), V::Float(..)) => (V::Float(x.to_f64()), b), + (V::Ratio(x), V::Complex(..)) => (V::Complex(x.to_f64().into()), b), + (V::Float(x), V::Complex(..)) => (V::Complex(x.into()), b), (V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())), (V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)), (V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())), - (V::Float(..), V::Ratio(y)) => (a, V::Float(ratio_to_f64(*y))), - (V::Complex(..), V::Ratio(y)) => (a, V::Complex(ratio_to_f64(*y).into())), + (V::Float(..), V::Ratio(y)) => (a, V::Float(y.to_f64())), + (V::Complex(..), V::Ratio(y)) => (a, V::Complex(y.to_f64().into())), (V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())), _ => (a, b), } @@ -183,7 +188,7 @@ impl Value { (V::Float(x), V::Float(y)) => Ok(V::Float(x.powf(y))), (V::Ratio(x), V::Ratio(y)) - => Ok(V::Float(ratio_to_f64(x).powf(ratio_to_f64(y)))), + => Ok(V::Float(x.to_f64().powf(y.to_f64()))), (V::Complex(x), V::Complex(y)) => Ok(V::Complex(x.powc(y))), (l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}") @@ -209,10 +214,10 @@ impl PartialEq for Value { (V::Float(a), V::Int(b)) => *a == *b as f64, (V::Int(a), V::Complex(b)) => Complex64::from(*a as f64) == *b, (V::Complex(a), V::Int(b)) => *a == Complex64::from(*b as f64), - (V::Ratio(a), V::Float(b)) => ratio_to_f64(*a) == *b, - (V::Float(a), V::Ratio(b)) => *a == ratio_to_f64(*b), - (V::Ratio(a), V::Complex(b)) => Complex64::from(ratio_to_f64(*a)) == *b, - (V::Complex(a), V::Ratio(b)) => *a == Complex64::from(ratio_to_f64(*b)), + (V::Ratio(a), V::Float(b)) => a.to_f64() == *b, + (V::Float(a), V::Ratio(b)) => *a == b.to_f64(), + (V::Ratio(a), V::Complex(b)) => Complex64::from(a.to_f64()) == *b, + (V::Complex(a), V::Ratio(b)) => *a == Complex64::from(b.to_f64()), (V::Float(a), V::Complex(b)) => Complex64::from(*a) == *b, (V::Complex(a), V::Float(b)) => *a == Complex64::from(*b), (V::String(a), V::String(b)) => *a == *b, @@ -246,8 +251,8 @@ impl PartialOrd for Value { (V::Ratio(a), V::Int(b)) => a.partial_cmp(&Rational64::from(*b)), (V::Int(a), V::Float(b)) => (*a as f64).partial_cmp(b), (V::Float(a), V::Int(b)) => a.partial_cmp(&(*b as f64)), - (V::Ratio(a), V::Float(b)) => ratio_to_f64(*a).partial_cmp(b), - (V::Float(a), V::Ratio(b)) => a.partial_cmp(&ratio_to_f64(*b)), + (V::Ratio(a), V::Float(b)) => a.to_f64().partial_cmp(b), + (V::Float(a), V::Ratio(b)) => a.partial_cmp(&b.to_f64()), (V::String(a), V::String(b)) => a.partial_cmp(b), (V::List(a), V::List(b)) => a.borrow().partial_cmp(&*b.borrow()), (V::Symbol(a), V::Symbol(b)) => a.partial_cmp(b), diff --git a/talc-lang/src/vm.rs b/talc-lang/src/vm.rs index 41524df..a34bcf9 100644 --- a/talc-lang/src/vm.rs +++ b/talc-lang/src/vm.rs @@ -296,7 +296,7 @@ impl Vm { // [a0,a1...an] -.> [[a0,a1...an]] I::NewList(n) => { let list = self.pop_n(n as usize); - self.push(list.into()) + self.push(list.into()); }, // [l,a0,a1...an] -.> [l ++ [a0,a1...an]] I::GrowList(n) => { @@ -314,7 +314,7 @@ impl Vm { let k = self.pop(); table.insert(k.try_into()?, v); } - self.push(table.into()) + self.push(table.into()); }, // [t,k0,v0...kn,vn] -> [t ++ {k0=v0...kn=vn}] I::GrowTable(n) => { @@ -348,17 +348,17 @@ impl Vm { // ip = n I::Jump(n) => { self.check_interrupt()?; - frame.ip = usize::from(n) + frame.ip = usize::from(n); }, // [v] ->, [], if v then ip = n I::JumpTrue(n) => if self.pop().truthy() { self.check_interrupt()?; - frame.ip = usize::from(n) + frame.ip = usize::from(n); }, // [v] ->, [], if not v then ip = n I::JumpFalse(n) => if !self.pop().truthy() { self.check_interrupt()?; - frame.ip = usize::from(n) + frame.ip = usize::from(n); }, // [v] -> [iter(v)] I::IterBegin => { @@ -368,12 +368,11 @@ impl Vm { // [i,cell(v)] -> [i,v] // [i,nil] -> [], ip = n I::IterTest(n) => { - match self.pop().iter_unpack() { - Some(v) => self.push(v), - None => { - self.pop(); - frame.ip = usize::from(n) - }, + if let Some(v) = self.pop().iter_unpack() { + self.push(v); + } else { + self.pop(); + frame.ip = usize::from(n); } }, // try_frames.push(t, stack.len()) diff --git a/talc-std/src/iter.rs b/talc-std/src/iter.rs index a93bfb2..11ba8b9 100644 --- a/talc-std/src/iter.rs +++ b/talc-std/src/iter.rs @@ -132,7 +132,7 @@ pub fn tee(_: &mut Vm, args: Vec) -> Result { match vmcalliter!(vm; iter.clone())? { Some(val) => { vmcall!(vm; tee.clone(), val.clone())?; - Ok(val.into()) + Ok(val) } None => Ok(Value::iter_pack(None)), } @@ -170,7 +170,7 @@ pub fn filter(_: &mut Vm, args: Vec) -> Result { }; let res = vmcall!(vm; filter.clone(), next.clone())?; if res.truthy() { - return Ok(next.into()) + return Ok(next) } } }; @@ -198,7 +198,7 @@ pub fn take(_: &mut Vm, args: Vec) -> Result { return Ok(Value::iter_pack(None)) }; *taken.borrow_mut() += 1; - Ok(next.into()) + Ok(next) }; Ok(NativeFunc::new(Box::new(f), 0).into()) } @@ -262,22 +262,19 @@ pub fn cycle(_: &mut Vm, args: Vec) -> Result { } let val = r.0[r.2].clone(); r.2 = (r.2 + 1) % r.0.len(); - return Ok(val.into()) + return Ok(val) } - match vmcalliter!(vm; iter.clone())? { - Some(v) => { - record.borrow_mut().0.push(v.clone()); - Ok(v.into()) - }, - None => { - let mut r = record.borrow_mut(); - r.1 = true; - if r.0.is_empty() { - Ok(Value::iter_pack(None)) - } else { - r.2 = (r.2 + 1) % r.0.len(); - Ok(r.0[0].clone().into()) - } + if let Some(v) = vmcalliter!(vm; iter.clone())? { + record.borrow_mut().0.push(v.clone()); + Ok(v) + } else { + let mut r = record.borrow_mut(); + r.1 = true; + if r.0.is_empty() { + Ok(Value::iter_pack(None)) + } else { + r.2 = (r.2 + 1) % r.0.len(); + Ok(r.0[0].clone()) } } }; @@ -337,7 +334,7 @@ pub fn rev(_: &mut Vm, args: Vec) -> Result { if lst.borrow().is_none() { let mut l = Vec::new(); while let Some(x) = vmcalliter!(vm; iter.clone())? { - l.push(x) + l.push(x); } *lst.borrow_mut() = Some(l); } @@ -392,12 +389,11 @@ pub fn alternate(_: &mut Vm, args: Vec) -> Result { let n = s.0; s.0 = (s.0 + 1) % iters.len(); drop(s); - match vmcalliter!(vm; iters[n].clone())? { - Some(v) => Ok(v), - None => { - state.borrow_mut().1 = true; - Ok(Value::iter_pack(None)) - } + if let Some(v) = vmcalliter!(vm; iters[n].clone())? { + Ok(v) + } else { + state.borrow_mut().1 = true; + Ok(Value::iter_pack(None)) } }; @@ -417,25 +413,23 @@ pub fn intersperse(_: &mut Vm, args: Vec) -> Result { let state = RefCell::new(Intersperse::Init); let f = move |vm: &mut Vm, _| { match state.take() { - Intersperse::Init => match vmcalliter!(vm; iter.clone())? { - Some(v) => { + Intersperse::Init => { + if let Some(v) = vmcalliter!(vm; iter.clone())? { *state.borrow_mut() = Intersperse::Waiting; Ok(v) - }, - None => { + } else { *state.borrow_mut() = Intersperse::End; Ok(Value::iter_pack(None)) - }, + } }, - Intersperse::Waiting => match vmcalliter!(vm; iter.clone())? { - Some(v) => { + Intersperse::Waiting => { + if let Some(v) = vmcalliter!(vm; iter.clone())? { *state.borrow_mut() = Intersperse::HasNext(v); Ok(val.clone()) - }, - None => { + } else { *state.borrow_mut() = Intersperse::End; Ok(Value::iter_pack(None)) - }, + } }, Intersperse::HasNext(v) => { *state.borrow_mut() = Intersperse::Waiting; @@ -460,12 +454,11 @@ pub fn chain(_: &mut Vm, args: Vec) -> Result { if *done_first.borrow() { return vmcall!(vm; iter2.clone()) } - match vmcalliter!(vm; iter1.clone())? { - Some(v) => Ok(v), - None => { - *done_first.borrow_mut() = true; - vmcall!(vm; iter2.clone()) - } + if let Some(v) = vmcalliter!(vm; iter1.clone())? { + Ok(v) + } else { + *done_first.borrow_mut() = true; + vmcall!(vm; iter2.clone()) } }; @@ -494,38 +487,36 @@ pub fn cartprod(_: &mut Vm, args: Vec) -> Result { } if state.borrow().b_idx >= state.borrow().b_data.len() { - if !state.borrow().b_done { - let v = vmcalliter!(vm; b.clone())?; - let mut s = state.borrow_mut(); - match v { - Some(x) => s.b_data.push(x), - None => { - s.b_done = true; - if s.b_idx == 0 { - s.done = true; - return Ok(Value::iter_pack(None)) - } - s.b_idx = 0; - } - }; - } else { + if state.borrow().b_done { if state.borrow().b_idx == 0 { state.borrow_mut().done = true; return Ok(Value::Nil) } state.borrow_mut().b_idx = 0; + } else { + let v = vmcalliter!(vm; b.clone())?; + let mut s = state.borrow_mut(); + if let Some(x) = v { + s.b_data.push(x) + } else { + s.b_done = true; + if s.b_idx == 0 { + s.done = true; + return Ok(Value::iter_pack(None)) + } + s.b_idx = 0; + } } } let b_res = state.borrow().b_data[state.borrow().b_idx].clone(); if state.borrow().b_idx == 0 { - match vmcalliter!(vm; a.clone())? { - Some(v) => state.borrow_mut().a_val = v, - None => { - state.borrow_mut().done = true; - return Ok(Value::iter_pack(None)) - } + if let Some(v) = vmcalliter!(vm; a.clone())? { + state.borrow_mut().a_val = v + } else { + state.borrow_mut().done = true; + return Ok(Value::iter_pack(None)) } } @@ -629,7 +620,7 @@ pub fn sum(vm: &mut Vm, args: Vec) -> Result { let mut result = Value::Int(0); while let Some(value) = vmcalliter!(vm; iter.clone())? { - result = (result + value)? + result = (result + value)?; } Ok(result) } @@ -641,7 +632,7 @@ pub fn prod(vm: &mut Vm, args: Vec) -> Result { let mut result = Value::Int(1); while let Some(value) = vmcalliter!(vm; iter.clone())? { - result = (result * value)? + result = (result * value)?; } Ok(result) } diff --git a/talc-std/src/num.rs b/talc-std/src/num.rs index 5b1d91e..ab6fc1a 100644 --- a/talc-std/src/num.rs +++ b/talc-std/src/num.rs @@ -124,10 +124,10 @@ fn to_radix_inner(n: i64, radix: u32) -> String { if n < 0 { result.push('-' as u32 as u8); begin = 1; - x = (-n) as u64 + x = (-n) as u64; } else { - x = n as u64 - }; + x = n as u64; + } loop { let m = x % (radix as u64); @@ -360,9 +360,8 @@ pub fn factors(_: &mut Vm, args: Vec) -> Result { x /= 3; factors.push(Value::Int(3)); } - //let lim = isqrt_inner(x); let mut i = 5; - while x > 1 { + while x >= i*i { while x % i == 0 { x /= i; factors.push(Value::Int(i)); @@ -374,6 +373,9 @@ pub fn factors(_: &mut Vm, args: Vec) -> Result { } i += 4; } + if x > 1 { + factors.push(Value::Int(x)); + } Ok(factors.into()) } diff --git a/talc-std/src/random.rs b/talc-std/src/random.rs index a27d884..ae5a4db 100644 --- a/talc-std/src/random.rs +++ b/talc-std/src/random.rs @@ -53,7 +53,7 @@ pub fn rand_in(vm: &mut Vm, args: Vec) -> Result { while let Some(v) = vmcalliter!(vm; iter.clone())? { values.push(v); } - if values.len() == 0 { + if values.is_empty() { throw!(*SYM_TYPE_ERROR, "rand_in: empty iterator") } let i = rand::thread_rng().gen_range(0..values.len()); diff --git a/talc-std/src/value.rs b/talc-std/src/value.rs index ad8cf01..83b1636 100644 --- a/talc-std/src/value.rs +++ b/talc-std/src/value.rs @@ -1,6 +1,6 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc}; -use talc_lang::{exception, parse_float, parse_int, symbol::SYM_TYPE_ERROR, throw, value::{HashValue, Rational64, Value}, Vm, exception::Result}; +use talc_lang::{exception::Result, parse_float, parse_int, symbol::SYM_TYPE_ERROR, throw, exception, value::{ops::RatioExt, HashValue, Rational64, Value}, Vm}; use talc_macros::native_func; use crate::unpack_args; @@ -49,7 +49,7 @@ pub fn as_(_: &mut Vm, args: Vec) -> Result { if val.get_type() == ty { return Ok(val) } - match (val, ty.name().as_ref()) { + match (val, ty.name()) { (_, "nil") => Ok(Value::Nil), (v, "string") => Ok(Value::String(v.to_string().into())), (v, "bool") => Ok(Value::Bool(v.truthy())), @@ -59,9 +59,9 @@ pub fn as_(_: &mut Vm, args: Vec) -> Result { (Value::Int(x), "ratio") => Ok(Value::Ratio(x.into())), (Value::Int(x), "float") => Ok(Value::Float(x as f64)), (Value::Int(x), "complex") => Ok(Value::Complex((x as f64).into())), - (Value::Ratio(x), "int") => Ok(Value::Int(*x.numer() / *x.denom())), - (Value::Ratio(x), "float") => Ok(Value::Float(*x.numer() as f64 / *x.denom() as f64)), - (Value::Ratio(x), "complex") => Ok(Value::Complex((*x.numer() as f64 / *x.denom() as f64).into())), + (Value::Ratio(x), "int") => Ok(Value::Int(x.to_integer())), + (Value::Ratio(x), "float") => Ok(Value::Float(x.to_f64())), + (Value::Ratio(x), "complex") => Ok(Value::Complex(x.to_f64().into())), (Value::Float(x), "int") => Ok(Value::Int(x as i64)), (Value::Float(x), "ratio") => { let r = Rational64::approximate_float(x)