From 732d0d475f393007e303e224adbbf321a213cbd3 Mon Sep 17 00:00:00 2001 From: David Peter Date: Sun, 23 Jun 2024 23:09:13 +0200 Subject: [PATCH 1/2] Remove unnecessary cloning in VM --- numbat/src/ffi/currency.rs | 7 ++++--- numbat/src/ffi/datetime.rs | 31 ++++++++++++++++--------------- numbat/src/ffi/functions.rs | 12 +++++++----- numbat/src/ffi/lists.rs | 25 +++++++++++++------------ numbat/src/ffi/lookup.rs | 5 +++-- numbat/src/ffi/macros.rs | 27 +++++++++++++++++---------- numbat/src/ffi/math.rs | 35 ++++++++++++++++++----------------- numbat/src/ffi/mod.rs | 8 ++++++-- numbat/src/ffi/procedures.rs | 25 +++++++++++++------------ numbat/src/ffi/strings.rs | 25 +++++++++++++------------ numbat/src/interpreter.rs | 2 +- numbat/src/value.rs | 22 ++++++++++++---------- numbat/src/vm.rs | 28 +++++++++++++--------------- 13 files changed, 136 insertions(+), 116 deletions(-) diff --git a/numbat/src/ffi/currency.rs b/numbat/src/ffi/currency.rs index 638455d2..5a0a42c2 100644 --- a/numbat/src/ffi/currency.rs +++ b/numbat/src/ffi/currency.rs @@ -1,13 +1,14 @@ use super::macros::*; +use super::Args; use super::Result; use crate::currency::ExchangeRatesCache; use crate::quantity::Quantity; use crate::value::Value; -pub fn exchange_rate(args: &[Value]) -> Result { - let rate = string_arg!(args, 0); +pub fn exchange_rate(mut args: Args) -> Result { + let rate = string_arg!(args); let exchange_rates = ExchangeRatesCache::new(); - return_scalar!(exchange_rates.get_rate(rate).unwrap_or(f64::NAN)) + return_scalar!(exchange_rates.get_rate(&rate).unwrap_or(f64::NAN)) } diff --git a/numbat/src/ffi/datetime.rs b/numbat/src/ffi/datetime.rs index deccf7eb..d5c91521 100644 --- a/numbat/src/ffi/datetime.rs +++ b/numbat/src/ffi/datetime.rs @@ -1,4 +1,5 @@ use super::macros::*; +use super::Args; use super::Result; use crate::datetime; use crate::quantity::Quantity; @@ -8,56 +9,56 @@ use crate::RuntimeError; use std::fmt::Write; -pub fn now(_args: &[Value]) -> Result { +pub fn now(_args: Args) -> Result { let now = chrono::Local::now().fixed_offset(); return_datetime!(now) } -pub fn datetime(args: &[Value]) -> Result { - let input = string_arg!(args, 0); +pub fn datetime(mut args: Args) -> Result { + let input = string_arg!(args); - let output = datetime::parse_datetime(input) + let output = datetime::parse_datetime(&input) .ok_or(RuntimeError::DateParsingErrorUnknown)? .fixed_offset(); return_datetime!(output) } -pub fn format_datetime(args: &[Value]) -> Result { - let format = string_arg!(args, 0); - let dt = datetime_arg!(args, 1); +pub fn format_datetime(mut args: Args) -> Result { + let format = string_arg!(args); + let dt = datetime_arg!(args); let mut output = String::new(); - write!(output, "{}", dt.format(format)).map_err(|_| RuntimeError::DateFormattingError)?; + write!(output, "{}", dt.format(&format)).map_err(|_| RuntimeError::DateFormattingError)?; return_string!(output) } -pub fn get_local_timezone(_args: &[Value]) -> Result { +pub fn get_local_timezone(_args: Args) -> Result { let local_tz = datetime::get_local_timezone_or_utc().to_string(); return_string!(local_tz) } -pub fn tz(args: &[Value]) -> Result { - let tz = string_arg!(args, 0); +pub fn tz(mut args: Args) -> Result { + let tz = string_arg!(args); Ok(Value::FunctionReference(FunctionReference::TzConversion( tz.into(), ))) } -pub fn unixtime(args: &[Value]) -> Result { - let input = datetime_arg!(args, 0); +pub fn unixtime(mut args: Args) -> Result { + let input = datetime_arg!(args); let output = input.timestamp(); return_scalar!(output as f64) } -pub fn from_unixtime(args: &[Value]) -> Result { - let timestamp = quantity_arg!(args, 0).unsafe_value().to_f64() as i64; +pub fn from_unixtime(mut args: Args) -> Result { + let timestamp = quantity_arg!(args).unsafe_value().to_f64() as i64; let dt = chrono::DateTime::from_timestamp(timestamp, 0) .ok_or(RuntimeError::DateTimeOutOfRange)? diff --git a/numbat/src/ffi/functions.rs b/numbat/src/ffi/functions.rs index b4f52866..2f386c32 100644 --- a/numbat/src/ffi/functions.rs +++ b/numbat/src/ffi/functions.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use std::sync::OnceLock; -use super::macros::*; +use super::{macros::*, Args}; use crate::{quantity::Quantity, value::Value, RuntimeError}; use super::{Callable, ForeignFunction, Result}; @@ -103,11 +103,13 @@ pub(crate) fn functions() -> &'static HashMap { }) } -fn error(args: &[Value]) -> Result { - Err(RuntimeError::UserError(args[0].unsafe_as_string().into())) +fn error(mut args: Args) -> Result { + Err(RuntimeError::UserError( + arg!(args).unsafe_as_string().into(), + )) } -fn unit_of(args: &[Value]) -> Result { - let input_unit = quantity_arg!(args, 0).unit().clone(); +fn unit_of(mut args: Args) -> Result { + let input_unit = quantity_arg!(args).unit().clone(); return_quantity!(1.0, input_unit) } diff --git a/numbat/src/ffi/lists.rs b/numbat/src/ffi/lists.rs index 19e1ce18..c50016c3 100644 --- a/numbat/src/ffi/lists.rs +++ b/numbat/src/ffi/lists.rs @@ -1,27 +1,27 @@ use super::macros::*; -use super::Result; +use super::{Args, Result}; use crate::quantity::Quantity; use crate::value::Value; use crate::RuntimeError; -pub fn len(args: &[Value]) -> Result { - let list = list_arg!(args, 0); +pub fn len(mut args: Args) -> Result { + let list = list_arg!(args); return_scalar!(list.len() as f64) } -pub fn head(args: &[Value]) -> Result { - let list = list_arg!(args, 0); +pub fn head(mut args: Args) -> Result { + let mut list = list_arg!(args); - if let Some(first) = list.first() { - Ok(first.clone()) + if let Some(first) = list.pop_front() { + Ok(first) } else { Err(RuntimeError::EmptyList) } } -pub fn tail(args: &[Value]) -> Result { - let mut list = list_arg!(args, 0); +pub fn tail(mut args: Args) -> Result { + let mut list = list_arg!(args); if list.is_empty() { Err(RuntimeError::EmptyList) @@ -32,9 +32,10 @@ pub fn tail(args: &[Value]) -> Result { } } -pub fn cons(args: &[Value]) -> Result { - let mut list = list_arg!(args, 1).clone(); - list.insert(0, args[0].clone()); +pub fn cons(mut args: Args) -> Result { + let element = arg!(args); + let mut list = list_arg!(args); + list.insert(0, element); return_list!(list) } diff --git a/numbat/src/ffi/lookup.rs b/numbat/src/ffi/lookup.rs index fc88582c..7a06e869 100644 --- a/numbat/src/ffi/lookup.rs +++ b/numbat/src/ffi/lookup.rs @@ -1,11 +1,12 @@ use super::macros::*; +use super::Args; use super::Result; use crate::quantity::Quantity; use crate::typed_ast::DType; use crate::value::Value; use crate::RuntimeError; -pub fn _get_chemical_element_data_raw(args: &[Value]) -> Result { +pub fn _get_chemical_element_data_raw(mut args: Args) -> Result { use crate::span::{SourceCodePositition, Span}; use crate::typed_ast::StructInfo; use crate::typed_ast::Type; @@ -13,7 +14,7 @@ pub fn _get_chemical_element_data_raw(args: &[Value]) -> Result { use mendeleev::{Electronvolt, GramPerCubicCentimeter, Kelvin, KiloJoulePerMole}; use std::sync::Arc; - let pattern = string_arg!(args, 0).to_lowercase(); + let pattern = string_arg!(args).to_lowercase(); if let Some(element) = mendeleev::Element::list() .iter() diff --git a/numbat/src/ffi/macros.rs b/numbat/src/ffi/macros.rs index 1a93532f..31ad35db 100644 --- a/numbat/src/ffi/macros.rs +++ b/numbat/src/ffi/macros.rs @@ -1,36 +1,43 @@ /// Some macros for writing FFI functions +macro_rules! arg { + ($args:ident) => { + $args.pop_front().unwrap() + }; +} +pub(crate) use arg; + macro_rules! quantity_arg { - ($args:ident, $index:expr) => { - $args[$index].unsafe_as_quantity() + ($args:ident) => { + arg!($args).unsafe_as_quantity() }; } pub(crate) use quantity_arg; macro_rules! scalar_arg { - ($args:ident, $index:expr) => { - quantity_arg!($args, $index).as_scalar().unwrap() + ($args:ident) => { + quantity_arg!($args).as_scalar().unwrap() }; } pub(crate) use scalar_arg; macro_rules! list_arg { - ($args:ident, $index:expr) => { - $args[$index].unsafe_as_list() + ($args:ident) => { + arg!($args).unsafe_as_list() }; } pub(crate) use list_arg; macro_rules! string_arg { - ($args:ident, $index:expr) => { - $args[$index].unsafe_as_string() + ($args:ident) => { + arg!($args).unsafe_as_string() }; } pub(crate) use string_arg; macro_rules! datetime_arg { - ($args:ident, $index:expr) => { - $args[$index].unsafe_as_datetime() + ($args:ident) => { + arg!($args).unsafe_as_datetime() }; } pub(crate) use datetime_arg; diff --git a/numbat/src/ffi/math.rs b/numbat/src/ffi/math.rs index d145b601..3fdb23d1 100644 --- a/numbat/src/ffi/math.rs +++ b/numbat/src/ffi/math.rs @@ -1,12 +1,13 @@ use super::macros::*; +use super::Args; use super::Result; use crate::quantity::Quantity; use crate::value::Value; -pub fn mod_(args: &[Value]) -> Result { - let x = quantity_arg!(args, 0); - let y = quantity_arg!(args, 1); +pub fn mod_(mut args: Args) -> Result { + let x = quantity_arg!(args); + let y = quantity_arg!(args); let x_value = x.unsafe_value().to_f64(); let y_value = y.convert_to(x.unit()).unwrap().unsafe_value().to_f64(); @@ -17,8 +18,8 @@ pub fn mod_(args: &[Value]) -> Result { // A simple math function with signature 'Dim D. Fn[(D) -> D]', which only operates on the value of the quantity macro_rules! simple_polymorphic_math_function { ($name:ident, $op:ident) => { - pub fn $name(args: &[Value]) -> Result { - let arg = quantity_arg!(args, 0); + pub fn $name(mut args: Args) -> Result { + let arg = quantity_arg!(args); let value = arg.unsafe_value().to_f64(); return_quantity!(value.$op(), arg.unit().clone()) @@ -29,8 +30,8 @@ macro_rules! simple_polymorphic_math_function { // Similar, but with signature 'Fn[(Scalar) -> Scalar]' macro_rules! simple_scalar_math_function { ($name:ident, $op:ident) => { - pub fn $name(args: &[Value]) -> Result { - let value = scalar_arg!(args, 0).to_f64(); + pub fn $name(mut args: Args) -> Result { + let value = scalar_arg!(args).to_f64(); return_scalar!(value.$op()) } }; @@ -48,9 +49,9 @@ simple_scalar_math_function!(asin, asin); simple_scalar_math_function!(acos, acos); simple_scalar_math_function!(atan, atan); -pub fn atan2(args: &[Value]) -> Result { - let y = quantity_arg!(args, 0); - let x = quantity_arg!(args, 1); +pub fn atan2(mut args: Args) -> Result { + let y = quantity_arg!(args); + let x = quantity_arg!(args); let y_value = y.unsafe_value().to_f64(); let x_value = x.convert_to(y.unit()).unwrap().unsafe_value().to_f64(); @@ -69,24 +70,24 @@ simple_scalar_math_function!(ln, ln); simple_scalar_math_function!(log10, log10); simple_scalar_math_function!(log2, log2); -pub fn gamma(args: &[Value]) -> Result { - let input = scalar_arg!(args, 0).to_f64(); +pub fn gamma(mut args: Args) -> Result { + let input = scalar_arg!(args).to_f64(); return_scalar!(crate::gamma::gamma(input)) } -pub fn is_nan(args: &[Value]) -> Result { - let arg = quantity_arg!(args, 0); +pub fn is_nan(mut args: Args) -> Result { + let arg = quantity_arg!(args); return_boolean!(arg.unsafe_value().to_f64().is_nan()) } -pub fn is_infinite(args: &[Value]) -> Result { - let arg = quantity_arg!(args, 0); +pub fn is_infinite(mut args: Args) -> Result { + let arg = quantity_arg!(args); return_boolean!(arg.unsafe_value().to_f64().is_infinite()) } -pub fn random(_args: &[Value]) -> Result { +pub fn random(_args: Args) -> Result { return_scalar!(rand::random::()) } diff --git a/numbat/src/ffi/mod.rs b/numbat/src/ffi/mod.rs index 0063df27..2a2b6e63 100644 --- a/numbat/src/ffi/mod.rs +++ b/numbat/src/ffi/mod.rs @@ -8,6 +8,8 @@ mod math; mod procedures; mod strings; +use std::collections::VecDeque; + use crate::interpreter::RuntimeError; use crate::value::Value; use crate::vm::ExecutionContext; @@ -18,11 +20,13 @@ pub(crate) type ArityRange = std::ops::RangeInclusive; type Result = std::result::Result; -type BoxedFunction = Box Result + Send + Sync>; +pub(crate) type Args = VecDeque; + +type BoxedFunction = Box Result + Send + Sync>; pub(crate) enum Callable { Function(BoxedFunction), - Procedure(fn(&mut ExecutionContext, &[Value]) -> ControlFlow), + Procedure(fn(&mut ExecutionContext, Args) -> ControlFlow), } pub(crate) struct ForeignFunction { diff --git a/numbat/src/ffi/procedures.rs b/numbat/src/ffi/procedures.rs index 1b1da2ef..22ce9a32 100644 --- a/numbat/src/ffi/procedures.rs +++ b/numbat/src/ffi/procedures.rs @@ -2,12 +2,13 @@ use std::collections::HashMap; use std::sync::OnceLock; +use super::macros::*; use crate::{ ast::ProcedureKind, ffi::ControlFlow, pretty_print::PrettyPrint, value::Value, vm::ExecutionContext, RuntimeError, }; -use super::{Callable, ForeignFunction}; +use super::{Args, Callable, ForeignFunction}; static FFI_PROCEDURES: OnceLock> = OnceLock::new(); @@ -45,7 +46,7 @@ pub(crate) fn procedures() -> &'static HashMap { }) } -fn print(ctx: &mut ExecutionContext, args: &[Value]) -> ControlFlow { +fn print(ctx: &mut ExecutionContext, args: Args) -> ControlFlow { assert!(args.len() <= 1); if args.is_empty() { @@ -60,22 +61,22 @@ fn print(ctx: &mut ExecutionContext, args: &[Value]) -> ControlFlow { ControlFlow::Continue(()) } -fn assert(_: &mut ExecutionContext, args: &[Value]) -> ControlFlow { +fn assert(_: &mut ExecutionContext, mut args: Args) -> ControlFlow { assert!(args.len() == 1); - if args[0].unsafe_as_bool() { + if arg!(args).unsafe_as_bool() { ControlFlow::Continue(()) } else { ControlFlow::Break(RuntimeError::AssertFailed) } } -fn assert_eq(_: &mut ExecutionContext, args: &[Value]) -> ControlFlow { +fn assert_eq(_: &mut ExecutionContext, mut args: Args) -> ControlFlow { assert!(args.len() == 2 || args.len() == 3); if args.len() == 2 { - let lhs = &args[0]; - let rhs = &args[1]; + let lhs = arg!(args); + let rhs = arg!(args); let error = ControlFlow::Break(RuntimeError::AssertEq2Failed(lhs.clone(), rhs.clone())); @@ -84,7 +85,7 @@ fn assert_eq(_: &mut ExecutionContext, args: &[Value]) -> ControlFlow { let rhs = rhs.unsafe_as_quantity(); if let Ok(args1_converted) = rhs.convert_to(lhs.unit()) { - if lhs == &args1_converted { + if lhs == args1_converted { ControlFlow::Continue(()) } else { error @@ -100,10 +101,10 @@ fn assert_eq(_: &mut ExecutionContext, args: &[Value]) -> ControlFlow { } } } else { - let lhs = args[0].unsafe_as_quantity(); - let rhs = args[1].unsafe_as_quantity(); - let result = lhs - rhs; - let eps = args[2].unsafe_as_quantity(); + let lhs = quantity_arg!(args); + let rhs = quantity_arg!(args); + let result = &lhs - &rhs; + let eps = quantity_arg!(args); match result { Ok(diff) => match diff.convert_to(eps.unit()) { diff --git a/numbat/src/ffi/strings.rs b/numbat/src/ffi/strings.rs index 85c4f446..d067469b 100644 --- a/numbat/src/ffi/strings.rs +++ b/numbat/src/ffi/strings.rs @@ -1,33 +1,34 @@ use super::macros::*; +use super::Args; use super::Result; use crate::quantity::Quantity; use crate::value::Value; -pub fn str_length(args: &[Value]) -> Result { - let len = string_arg!(args, 0).len(); +pub fn str_length(mut args: Args) -> Result { + let len = string_arg!(args).len(); return_scalar!(len as f64) } -pub fn lowercase(args: &[Value]) -> Result { - return_string!(string_arg!(args, 0).to_lowercase()) +pub fn lowercase(mut args: Args) -> Result { + return_string!(string_arg!(args).to_lowercase()) } -pub fn uppercase(args: &[Value]) -> Result { - return_string!(string_arg!(args, 0).to_uppercase()) +pub fn uppercase(mut args: Args) -> Result { + return_string!(string_arg!(args).to_uppercase()) } -pub fn str_slice(args: &[Value]) -> Result { - let input = string_arg!(args, 0); - let start = quantity_arg!(args, 1).unsafe_value().to_f64() as usize; - let end = quantity_arg!(args, 2).unsafe_value().to_f64() as usize; +pub fn str_slice(mut args: Args) -> Result { + let input = string_arg!(args); + let start = quantity_arg!(args).unsafe_value().to_f64() as usize; + let end = quantity_arg!(args).unsafe_value().to_f64() as usize; let output = input.get(start..end).unwrap_or_default(); return_string!(output) } -pub fn chr(args: &[Value]) -> Result { - let idx = quantity_arg!(args, 0).unsafe_value().to_f64() as u32; +pub fn chr(mut args: Args) -> Result { + let idx = quantity_arg!(args).unsafe_value().to_f64() as u32; let output = char::from_u32(idx).unwrap_or('�'); diff --git a/numbat/src/interpreter.rs b/numbat/src/interpreter.rs index 071c37a5..337826be 100644 --- a/numbat/src/interpreter.rs +++ b/numbat/src/interpreter.rs @@ -215,7 +215,7 @@ mod tests { fn assert_evaluates_to(input: &str, expected: Quantity) { if let InterpreterResult::Value(actual) = get_interpreter_result(input).unwrap() { let actual = actual.unsafe_as_quantity(); - assert_eq!(actual, &expected); + assert_eq!(actual, expected); } else { panic!(); } diff --git a/numbat/src/value.rs b/numbat/src/value.rs index f8b71e46..7739eb6d 100644 --- a/numbat/src/value.rs +++ b/numbat/src/value.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{collections::VecDeque, sync::Arc}; use itertools::Itertools; @@ -24,6 +24,8 @@ impl std::fmt::Display for FunctionReference { } } +pub type NumbatList = VecDeque; + #[derive(Debug, Clone, PartialEq, Eq)] pub enum Value { Quantity(Quantity), @@ -34,12 +36,12 @@ pub enum Value { FunctionReference(FunctionReference), FormatSpecifiers(Option), StructInstance(Arc, Vec), - List(Vec), + List(NumbatList), } impl Value { #[track_caller] - pub fn unsafe_as_quantity(&self) -> &Quantity { + pub fn unsafe_as_quantity(self) -> Quantity { if let Value::Quantity(q) = self { q } else { @@ -48,16 +50,16 @@ impl Value { } #[track_caller] - pub fn unsafe_as_bool(&self) -> bool { + pub fn unsafe_as_bool(self) -> bool { if let Value::Boolean(b) = self { - *b + b } else { panic!("Expected value to be a bool"); } } #[track_caller] - pub fn unsafe_as_string(&self) -> &str { + pub fn unsafe_as_string(self) -> String { if let Value::String(s) = self { s } else { @@ -66,7 +68,7 @@ impl Value { } #[track_caller] - pub fn unsafe_as_datetime(&self) -> &chrono::DateTime { + pub fn unsafe_as_datetime(self) -> chrono::DateTime { if let Value::DateTime(dt) = self { dt } else { @@ -75,7 +77,7 @@ impl Value { } #[track_caller] - pub fn unsafe_as_function_reference(&self) -> &FunctionReference { + pub fn unsafe_as_function_reference(self) -> FunctionReference { if let Value::FunctionReference(inner) = self { inner } else { @@ -93,9 +95,9 @@ impl Value { } #[track_caller] - pub fn unsafe_as_list(&self) -> Vec { + pub fn unsafe_as_list(self) -> NumbatList { if let Value::List(values) = self { - values.clone() + values } else { panic!("Expected value to be a list"); } diff --git a/numbat/src/vm.rs b/numbat/src/vm.rs index 89183584..292097af 100644 --- a/numbat/src/vm.rs +++ b/numbat/src/vm.rs @@ -1,10 +1,11 @@ -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use std::sync::Arc; use std::{cmp::Ordering, fmt::Display}; use indexmap::IndexMap; use crate::typed_ast::StructInfo; +use crate::value::NumbatList; use crate::{ ffi::{self, ArityRange, Callable, ForeignFunction}, interpreter::{InterpreterResult, PrintFunction, Result, RuntimeError}, @@ -820,19 +821,18 @@ impl Vm { debug_assert!(foreign_function.arity.contains(&num_args)); - let mut args = vec![]; + let mut args = VecDeque::new(); for _ in 0..num_args { - args.push(self.pop()); + args.push_front(self.pop()); } - args.reverse(); // TODO: use a deque? match &self.ffi_callables[function_idx].callable { Callable::Function(function) => { - let result = (function)(&args[..]); + let result = (function)(args); self.push(result?); } Callable::Procedure(procedure) => { - let result = (procedure)(ctx, &args[..]); + let result = (procedure)(ctx, args); match result { std::ops::ControlFlow::Continue(()) => {} @@ -848,7 +848,7 @@ impl Vm { let callable = self.pop(); match callable.unsafe_as_function_reference() { - FunctionReference::Normal(name) => { + FunctionReference::Normal(ref name) => { let function_idx = self.get_function_idx(name) as usize; // TODO: unify code with 'Op::Call'? @@ -858,21 +858,20 @@ impl Vm { fp: self.stack.len() - num_args, }) } - FunctionReference::Foreign(name) => { + FunctionReference::Foreign(ref name) => { let function_idx = self .get_ffi_callable_idx(name) .expect("Foreign function exists") as usize; - let mut args = vec![]; + let mut args = VecDeque::new(); for _ in 0..num_args { - args.push(self.pop()); + args.push_front(self.pop()); } - args.reverse(); match &self.ffi_callables[function_idx].callable { Callable::Function(function) => { - let result = (function)(&args[..]); + let result = (function)(args); self.push(result?); } Callable::Procedure(..) => unreachable!("Foreign procedures can not be targeted by a function reference"), @@ -1011,12 +1010,11 @@ impl Vm { } Op::BuildList => { let length = self.read_u16(); - let mut list = Vec::with_capacity(length as usize); + let mut list = NumbatList::with_capacity(length as usize); for _ in 0..length { - list.push(self.pop()); + list.push_front(self.pop()); } - list.reverse(); self.stack.push(Value::List(list)); } From d5aab2fbe3ab5809186eb2befa83fd8c4f6b2f1a Mon Sep 17 00:00:00 2001 From: David Peter Date: Sun, 23 Jun 2024 23:26:01 +0200 Subject: [PATCH 2/2] Minor adaptations --- numbat/src/ffi/lists.rs | 8 +++----- numbat/src/ffi/procedures.rs | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/numbat/src/ffi/lists.rs b/numbat/src/ffi/lists.rs index c50016c3..6d9bc1e7 100644 --- a/numbat/src/ffi/lists.rs +++ b/numbat/src/ffi/lists.rs @@ -23,12 +23,10 @@ pub fn head(mut args: Args) -> Result { pub fn tail(mut args: Args) -> Result { let mut list = list_arg!(args); - if list.is_empty() { - Err(RuntimeError::EmptyList) - } else { - list.remove(0); - + if list.remove(0).is_some() { return_list!(list) + } else { + Err(RuntimeError::EmptyList) } } diff --git a/numbat/src/ffi/procedures.rs b/numbat/src/ffi/procedures.rs index 22ce9a32..7751189e 100644 --- a/numbat/src/ffi/procedures.rs +++ b/numbat/src/ffi/procedures.rs @@ -46,13 +46,13 @@ pub(crate) fn procedures() -> &'static HashMap { }) } -fn print(ctx: &mut ExecutionContext, args: Args) -> ControlFlow { +fn print(ctx: &mut ExecutionContext, mut args: Args) -> ControlFlow { assert!(args.len() <= 1); if args.is_empty() { (ctx.print_fn)(&crate::markup::text("")) } else { - match &args[0] { + match arg!(args) { Value::String(string) => (ctx.print_fn)(&crate::markup::text(string)), // print string without quotes arg => (ctx.print_fn)(&arg.pretty_print()), }