diff --git a/crates/rune/src/compile/v1/assemble.rs b/crates/rune/src/compile/v1/assemble.rs index 0d351028a..79bb76491 100644 --- a/crates/rune/src/compile/v1/assemble.rs +++ b/crates/rune/src/compile/v1/assemble.rs @@ -984,7 +984,7 @@ fn block_without_scope<'a, 'hir>( Ok(Asm::new(hir, ())) } -/// Assemble #[builtin] format_args!(...) macro. +/// Assemble #[builtin] format!(...) macro. #[instrument_ast(span = format)] fn builtin_format<'a, 'hir>( cx: &mut Ctxt<'a, 'hir, '_>, diff --git a/crates/rune/src/exported_macros.rs b/crates/rune/src/exported_macros.rs index 3abd5cc0c..a57fa4fdc 100644 --- a/crates/rune/src/exported_macros.rs +++ b/crates/rune/src/exported_macros.rs @@ -54,9 +54,7 @@ macro_rules! vm_write { ($($tt:tt)*) => { match core::write!($($tt)*) { Ok(()) => $crate::runtime::VmResult::Ok(()), - Err(err) => { - return $crate::runtime::VmResult::Err($crate::runtime::VmError::from(err)); - } + Err(err) => $crate::runtime::VmResult::Err($crate::runtime::VmError::from(err)), } }; } diff --git a/crates/rune/src/modules/fmt.rs b/crates/rune/src/modules/fmt.rs index 13ad52897..83b84f4bc 100644 --- a/crates/rune/src/modules/fmt.rs +++ b/crates/rune/src/modules/fmt.rs @@ -4,10 +4,11 @@ use core::fmt; use crate as rune; use crate::alloc::fmt::TryWrite; +use crate::alloc::prelude::*; use crate::compile; use crate::macros::{FormatArgs, MacroContext, TokenStream}; use crate::parse::Parser; -use crate::runtime::{Format, Formatter, VmResult}; +use crate::runtime::{EnvProtocolCaller, Format, Formatter, VmResult}; use crate::{ContextError, Module}; /// Formatting text. @@ -23,14 +24,20 @@ use crate::{ContextError, Module}; /// ``` #[rune::module(::std::fmt)] pub fn module() -> Result { - let mut module = Module::from_meta(self::module_meta)?.with_unique("std::fmt"); - - module.ty::()?; - module.ty::()?; - module.ty::()?; - module.function_meta(fmt_error_string_display)?; - module.macro_meta(format)?; - Ok(module) + let mut m = Module::from_meta(self::module_meta)?.with_unique("std::fmt"); + + m.ty::()?; + m.ty::()?; + m.function_meta(fmt_error_string_display)?; + m.macro_meta(format)?; + + m.ty::()?; + m.function_meta(format_string_display__meta)?; + m.function_meta(format_string_debug__meta)?; + m.function_meta(format_clone__meta)?; + m.implement_trait::(rune::item!(::std::clone::Clone))?; + + Ok(m) } #[rune::function(instance, protocol = STRING_DISPLAY)] @@ -58,3 +65,48 @@ pub(crate) fn format( let expanded = args.expand(cx)?; Ok(expanded.into_token_stream(cx)?) } + +/// Write a display representation of a format specification. +/// +/// # Examples +/// +/// ```rune +/// let value = #[builtin] format!("Hello", fill = '0', width = 10); +/// assert_eq!(format!("{value}"), "Hello00000"); +/// ``` +#[rune::function(keep, instance, protocol = STRING_DISPLAY)] +fn format_string_display(format: &Format, f: &mut Formatter) -> VmResult<()> { + VmResult::Ok(vm_try!(format.spec.format( + &format.value, + f, + &mut EnvProtocolCaller + ))) +} + +/// Write a debug representation of a format specification. +/// +/// # Examples +/// +/// ```rune +/// let value = #[builtin] format!("Hello", fill = '0', width = 10); +/// let string = format!("{value:?}"); +/// assert!(string is String); +/// ``` +#[rune::function(keep, instance, protocol = STRING_DEBUG)] +fn format_string_debug(format: &Format, f: &mut Formatter) -> VmResult<()> { + VmResult::Ok(vm_try!(vm_write!(f, "{format:?}"))) +} + +/// Clones a format specification. +/// +/// # Examples +/// +/// ```rune +/// let value = #[builtin] format!("Hello", fill = '0', width = 10); +/// let vlaue2 = value.clone(); +/// assert_eq!(format!("{value}"), "Hello00000"); +/// ``` +#[rune::function(keep, instance, protocol = CLONE)] +fn format_clone(this: &Format) -> VmResult { + VmResult::Ok(vm_try!(this.try_clone())) +} diff --git a/crates/rune/src/modules/ops.rs b/crates/rune/src/modules/ops.rs index 3ed055811..b51055214 100644 --- a/crates/rune/src/modules/ops.rs +++ b/crates/rune/src/modules/ops.rs @@ -179,6 +179,17 @@ pub fn module() -> Result { { m.ty::()?; + + m.function_meta(ControlFlow::partial_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + + m.function_meta(ControlFlow::eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + + m.function_meta(ControlFlow::string_debug__meta)?; + + m.function_meta(ControlFlow::clone__meta)?; + m.implement_trait::(rune::item!(::std::clone::Clone))?; } m.ty::()?; diff --git a/crates/rune/src/runtime/control_flow.rs b/crates/rune/src/runtime/control_flow.rs index 39af9a2e2..888371457 100644 --- a/crates/rune/src/runtime/control_flow.rs +++ b/crates/rune/src/runtime/control_flow.rs @@ -1,10 +1,12 @@ use core::ops; +use crate as rune; use crate::alloc::clone::TryClone; use crate::alloc::fmt::TryWrite; -use crate::runtime::{Formatter, FromValue, ProtocolCaller, ToValue, Value, VmResult}; use crate::Any; +use super::{EnvProtocolCaller, Formatter, FromValue, ProtocolCaller, ToValue, Value, VmResult}; + /// Used to tell an operation whether it should exit early or go on as usual. /// /// This acts as the basis of the [`TRY`] protocol in Rune. @@ -21,9 +23,8 @@ use crate::Any; /// assert_eq!(c, ControlFlow::Continue(42)); /// ``` #[derive(Debug, Clone, TryClone, Any)] -#[rune(crate)] #[try_clone(crate)] -#[rune(builtin, static_type = CONTROL_FLOW)] +#[rune(static_type = CONTROL_FLOW)] pub enum ControlFlow { /// Move on to the next phase of the operation as normal. #[rune(constructor)] @@ -34,25 +35,29 @@ pub enum ControlFlow { } impl ControlFlow { - pub(crate) fn string_debug_with( - &self, - f: &mut Formatter, - caller: &mut dyn ProtocolCaller, - ) -> VmResult<()> { - match self { - ControlFlow::Continue(value) => { - vm_try!(vm_write!(f, "Continue(")); - vm_try!(Value::string_debug_with(value, f, caller)); - vm_try!(vm_write!(f, ")")); - } - ControlFlow::Break(value) => { - vm_try!(vm_write!(f, "Break(")); - vm_try!(Value::string_debug_with(value, f, caller)); - vm_try!(vm_write!(f, ")")); - } - } - - VmResult::Ok(()) + /// Test two control flows for partial equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::partial_eq; + /// + /// assert_eq! { + /// partial_eq(ControlFlow::Continue(true), ControlFlow::Continue(true)), + /// true + /// }; + /// assert_eq! { + /// partial_eq(ControlFlow::Continue(true), ControlFlow::Break(false)), + /// false + /// }; + /// assert_eq! { + /// partial_eq(ControlFlow::Break(false), ControlFlow::Continue(true)), + /// false + /// }; + /// ``` + #[rune::function(keep, protocol = PARTIAL_EQ)] + pub(crate) fn partial_eq(&self, other: &Self) -> VmResult { + Self::partial_eq_with(self, other, &mut EnvProtocolCaller) } pub(crate) fn partial_eq_with( @@ -69,6 +74,31 @@ impl ControlFlow { } } + /// Test two control flows for total equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::eq; + /// + /// assert_eq! { + /// eq(ControlFlow::Continue(true), ControlFlow::Continue(true)), + /// true + /// }; + /// assert_eq! { + /// eq(ControlFlow::Continue(true), ControlFlow::Break(false)), + /// false + /// }; + /// assert_eq! { + /// eq(ControlFlow::Break(false), ControlFlow::Continue(true)), + /// false + /// }; + /// ``` + #[rune::function(keep, protocol = EQ)] + pub(crate) fn eq(&self, other: &ControlFlow) -> VmResult { + self.eq_with(other, &mut EnvProtocolCaller) + } + pub(crate) fn eq_with( &self, other: &ControlFlow, @@ -80,14 +110,55 @@ impl ControlFlow { _ => VmResult::Ok(false), } } -} -from_value2!( - ControlFlow, - into_control_flow_ref, - into_control_flow_mut, - into_control_flow -); + /// Debug print the control flow. + /// + /// # Examples + /// + /// ```rune + /// let string = format!("{:?}", ControlFlow::Continue(true)); + /// ``` + #[rune::function(keep, protocol = STRING_DEBUG)] + pub(crate) fn string_debug(&self, f: &mut Formatter) -> VmResult<()> { + Self::string_debug_with(self, f, &mut EnvProtocolCaller) + } + + pub(crate) fn string_debug_with( + &self, + f: &mut Formatter, + caller: &mut dyn ProtocolCaller, + ) -> VmResult<()> { + match self { + ControlFlow::Continue(value) => { + vm_try!(vm_write!(f, "Continue(")); + vm_try!(Value::string_debug_with(value, f, caller)); + vm_try!(vm_write!(f, ")")); + } + ControlFlow::Break(value) => { + vm_try!(vm_write!(f, "Break(")); + vm_try!(Value::string_debug_with(value, f, caller)); + vm_try!(vm_write!(f, ")")); + } + } + + VmResult::Ok(()) + } + + /// Clone the control flow. + /// + /// # Examples + /// + /// ```rune + /// let flow = ControlFlow::Continue("Hello World"); + /// let flow2 = flow.clone(); + /// + /// assert_eq!(flow, flow2); + /// ``` + #[rune::function(keep, protocol = CLONE)] + pub(crate) fn clone(&self) -> VmResult { + VmResult::Ok(vm_try!(self.try_clone())) + } +} impl ToValue for ops::ControlFlow where @@ -114,11 +185,13 @@ where { #[inline] fn from_value(value: Value) -> VmResult { - VmResult::Ok(match vm_try!(value.into_control_flow()) { + VmResult::Ok(match &*vm_try!(value.borrow_ref::()) { ControlFlow::Continue(value) => { - ops::ControlFlow::Continue(vm_try!(C::from_value(value))) + ops::ControlFlow::Continue(vm_try!(C::from_value(value.clone()))) + } + ControlFlow::Break(value) => { + ops::ControlFlow::Break(vm_try!(B::from_value(value.clone()))) } - ControlFlow::Break(value) => ops::ControlFlow::Break(vm_try!(B::from_value(value))), }) } } diff --git a/crates/rune/src/runtime/format.rs b/crates/rune/src/runtime/format.rs index fde6bbf0a..1ddf1d7cc 100644 --- a/crates/rune/src/runtime/format.rs +++ b/crates/rune/src/runtime/format.rs @@ -41,16 +41,15 @@ impl fmt::Display for AlignmentFromStrError { /// A format specification, wrapping an inner value. #[derive(Any, Debug, Clone, TryClone)] -#[rune(builtin, static_type = FORMAT)] +#[rune(static_type = FORMAT)] pub struct Format { /// The value being formatted. pub(crate) value: Value, /// The specification. + #[try_clone(copy)] pub(crate) spec: FormatSpec, } -from_value2!(Format, into_format_ref, into_format_mut, into_format); - /// A format specification. #[derive(Debug, Clone, Copy, TryClone, Serialize, Deserialize, Decode, Encode)] #[try_clone(copy)] diff --git a/crates/rune/src/runtime/mod.rs b/crates/rune/src/runtime/mod.rs index 28fd69fb4..cbc560481 100644 --- a/crates/rune/src/runtime/mod.rs +++ b/crates/rune/src/runtime/mod.rs @@ -184,6 +184,7 @@ mod vec_tuple; pub use self::vec_tuple::VecTuple; mod vm; +use self::vm::CallResultOnly; pub use self::vm::{CallFrame, Isolated, Vm}; mod vm_call; diff --git a/crates/rune/src/runtime/value.rs b/crates/rune/src/runtime/value.rs index 0ed7828c5..56e2a9ea3 100644 --- a/crates/rune/src/runtime/value.rs +++ b/crates/rune/src/runtime/value.rs @@ -20,18 +20,18 @@ use crate::alloc::fmt::TryWrite; use crate::alloc::prelude::*; use crate::alloc::{self, String}; use crate::compile::meta; -use crate::runtime::static_type; -use crate::runtime::vm::CallResultOnly; -use crate::runtime::{ - AccessError, AnyObj, AnyObjDrop, BorrowMut, BorrowRef, Bytes, ConstValue, ControlFlow, - DynGuardedArgs, EnvProtocolCaller, Format, Formatter, FromValue, Function, Future, Generator, - GeneratorState, IntoOutput, Iterator, MaybeTypeOf, Mut, Object, OwnedTuple, Protocol, - ProtocolCaller, RawAnyObjGuard, Ref, RuntimeError, Shared, Snapshot, Stream, ToValue, Type, - TypeInfo, Variant, Vec, Vm, VmErrorKind, VmIntegerRepr, VmResult, +use crate::{Any, Hash}; + +use super::static_type; +use super::{ + AccessError, AnyObj, AnyObjDrop, BorrowMut, BorrowRef, Bytes, CallResultOnly, ConstValue, + ControlFlow, DynGuardedArgs, EnvProtocolCaller, Format, Formatter, FromValue, Function, Future, + Generator, GeneratorState, IntoOutput, Iterator, MaybeTypeOf, Mut, Object, OwnedTuple, + Protocol, ProtocolCaller, RawAnyObjGuard, Ref, RuntimeError, Shared, Snapshot, Stream, ToValue, + Type, TypeInfo, Variant, Vec, Vm, VmErrorKind, VmIntegerRepr, VmResult, }; #[cfg(feature = "alloc")] -use crate::runtime::{Hasher, Tuple}; -use crate::{Any, Hash}; +use super::{Hasher, Tuple}; /// Defined guard for a reference value. /// @@ -370,14 +370,6 @@ impl Value { break 'fallback; } }, - BorrowRefRepr::Mutable(value) => match &*value { - Mutable::Format(format) => { - vm_try!(format.spec.format(&format.value, f, caller)); - } - _ => { - break 'fallback; - } - }, _ => { break 'fallback; } @@ -422,7 +414,6 @@ impl Value { Mutable::Vec(value) => Mutable::Vec(vm_try!(value.try_clone())), Mutable::Tuple(value) => Mutable::Tuple(vm_try!(value.try_clone())), Mutable::Object(value) => Mutable::Object(vm_try!(value.try_clone())), - Mutable::ControlFlow(value) => Mutable::ControlFlow(vm_try!(value.try_clone())), Mutable::Stream(value) => Mutable::Stream(vm_try!(value.try_clone())), Mutable::Generator(value) => Mutable::Generator(vm_try!(value.try_clone())), Mutable::GeneratorState(value) => { @@ -435,7 +426,6 @@ impl Value { Mutable::Struct(value) => Mutable::Struct(vm_try!(value.try_clone())), Mutable::Variant(value) => Mutable::Variant(vm_try!(value.try_clone())), Mutable::Function(value) => Mutable::Function(vm_try!(value.try_clone())), - Mutable::Format(value) => Mutable::Format(vm_try!(value.try_clone())), _ => { break 'fallback; } @@ -501,9 +491,6 @@ impl Value { Mutable::Object(value) => { vm_try!(vm_write!(f, "{value:?}")); } - Mutable::ControlFlow(value) => { - vm_try!(ControlFlow::string_debug_with(value, f, caller)); - } Mutable::Future(value) => { vm_try!(vm_write!(f, "{value:?}")); } @@ -537,9 +524,6 @@ impl Value { Mutable::Function(value) => { vm_try!(vm_write!(f, "{value:?}")); } - Mutable::Format(value) => { - vm_try!(vm_write!(f, "{value:?}")); - } }; return VmResult::Ok(()); @@ -833,16 +817,6 @@ impl Value { into_vec, } - into! { - /// Coerce into a [`ControlFlow`]. - ControlFlow(ControlFlow), - into_control_flow_ref, - into_control_flow_mut, - borrow_control_flow_ref, - borrow_control_flow_mut, - into_control_flow, - } - into! { /// Coerce into a [`Function`]. Function(Function), @@ -873,16 +847,6 @@ impl Value { into_generator, } - into! { - /// Coerce into a [`Format`]. - Format(Format), - into_format_ref, - into_format_mut, - borrow_format_ref, - borrow_format_mut, - into_format, - } - into! { /// Coerce into [`Tuple`]. Tuple(OwnedTuple), @@ -1164,9 +1128,6 @@ impl Value { }); } (BorrowRefRepr::Mutable(a), BorrowRefRepr::Mutable(b2)) => match (&**a, &*b2) { - (Mutable::ControlFlow(a), Mutable::ControlFlow(b)) => { - return ControlFlow::partial_eq_with(a, b, caller); - } (Mutable::EmptyStruct(a), Mutable::EmptyStruct(b)) => { if a.rtti.hash == b.rtti.hash { // NB: don't get any future ideas, this must fall through to @@ -1358,9 +1319,6 @@ impl Value { (Mutable::Object(a), Mutable::Object(b)) => { return Object::eq_with(a, b, Value::eq_with, caller); } - (Mutable::ControlFlow(a), Mutable::ControlFlow(b)) => { - return ControlFlow::eq_with(a, b, caller); - } (Mutable::EmptyStruct(a), Mutable::EmptyStruct(b)) => { if a.rtti.hash == b.rtti.hash { // NB: don't get any future ideas, this must fall through to @@ -1962,7 +1920,6 @@ inline_from! { } from! { - ControlFlow => ControlFlow, Function => Function, GeneratorState => GeneratorState, Vec => Vec, @@ -1973,7 +1930,6 @@ from! { Object => Object, Tuple => OwnedTuple, Generator => Generator, - Format => Format, Future => Future, Stream => Stream, } @@ -1981,6 +1937,8 @@ from! { any_from! { String, Bytes, + Format, + ControlFlow, } from_container! { @@ -2259,8 +2217,6 @@ pub(crate) enum Mutable { Tuple(OwnedTuple), /// An object. Object(Object), - /// A control flow indicator. - ControlFlow(ControlFlow), /// A stored future. Future(Future), /// A Stream. @@ -2283,8 +2239,6 @@ pub(crate) enum Mutable { Variant(Variant), /// A stored function pointer. Function(Function), - /// A value being formatted. - Format(Format), } impl Mutable { @@ -2293,7 +2247,6 @@ impl Mutable { Mutable::Vec(..) => TypeInfo::static_type(static_type::VEC), Mutable::Tuple(..) => TypeInfo::static_type(static_type::TUPLE), Mutable::Object(..) => TypeInfo::static_type(static_type::OBJECT), - Mutable::ControlFlow(..) => TypeInfo::static_type(static_type::CONTROL_FLOW), Mutable::Future(..) => TypeInfo::static_type(static_type::FUTURE), Mutable::Stream(..) => TypeInfo::static_type(static_type::STREAM), Mutable::Generator(..) => TypeInfo::static_type(static_type::GENERATOR), @@ -2301,7 +2254,6 @@ impl Mutable { Mutable::Option(..) => TypeInfo::static_type(static_type::OPTION), Mutable::Result(..) => TypeInfo::static_type(static_type::RESULT), Mutable::Function(..) => TypeInfo::static_type(static_type::FUNCTION), - Mutable::Format(..) => TypeInfo::static_type(static_type::FORMAT), Mutable::EmptyStruct(empty) => empty.type_info(), Mutable::TupleStruct(tuple) => tuple.type_info(), Mutable::Struct(object) => object.type_info(), @@ -2318,7 +2270,6 @@ impl Mutable { Mutable::Vec(..) => static_type::VEC.hash, Mutable::Tuple(..) => static_type::TUPLE.hash, Mutable::Object(..) => static_type::OBJECT.hash, - Mutable::ControlFlow(..) => static_type::CONTROL_FLOW.hash, Mutable::Future(..) => static_type::FUTURE.hash, Mutable::Stream(..) => static_type::STREAM.hash, Mutable::Generator(..) => static_type::GENERATOR.hash, @@ -2326,7 +2277,6 @@ impl Mutable { Mutable::Result(..) => static_type::RESULT.hash, Mutable::Option(..) => static_type::OPTION.hash, Mutable::Function(..) => static_type::FUNCTION.hash, - Mutable::Format(..) => static_type::FORMAT.hash, Mutable::EmptyStruct(empty) => empty.rtti.hash, Mutable::TupleStruct(tuple) => tuple.rtti.hash, Mutable::Struct(object) => object.rtti.hash, diff --git a/crates/rune/src/runtime/value/serde.rs b/crates/rune/src/runtime/value/serde.rs index 89203eab8..b851dbf85 100644 --- a/crates/rune/src/runtime/value/serde.rs +++ b/crates/rune/src/runtime/value/serde.rs @@ -84,12 +84,6 @@ impl ser::Serialize for Value { Mutable::Function(..) => { Err(ser::Error::custom("cannot serialize function pointers")) } - Mutable::Format(..) => { - Err(ser::Error::custom("cannot serialize format specifications")) - } - Mutable::ControlFlow(..) => { - Err(ser::Error::custom("cannot serialize `start..end` ranges")) - } }, BorrowRefRepr::Any(value) => match value.type_hash() { String::HASH => { diff --git a/crates/rune/src/runtime/vm.rs b/crates/rune/src/runtime/vm.rs index 7293b6e0c..829291827 100644 --- a/crates/rune/src/runtime/vm.rs +++ b/crates/rune/src/runtime/vm.rs @@ -2966,7 +2966,7 @@ impl Vm { #[cfg_attr(feature = "bench", inline(never))] fn op_format(&mut self, addr: InstAddress, spec: FormatSpec, out: Output) -> VmResult<()> { let value = vm_try!(self.stack.at(addr)).clone(); - vm_try!(out.store(&mut self.stack, Mutable::Format(Format { value, spec }))); + vm_try!(out.store(&mut self.stack, || Format { value, spec })); VmResult::Ok(()) }