diff --git a/benches/benches/benchmarks/aoc_2020_19b.rs b/benches/benches/benchmarks/aoc_2020_19b.rs index 910c62168..c12a51497 100644 --- a/benches/benches/benchmarks/aoc_2020_19b.rs +++ b/benches/benches/benchmarks/aoc_2020_19b.rs @@ -16,7 +16,7 @@ fn aoc_2020_19b(b: &mut Criterion) { let mut vm = rune_vm! { use std::collections::HashMap; fn get_rules() { - HashMap::from([ + HashMap::from_iter([ (118, Rule::Or(Rule::Seq([29, 95]), Rule::Seq([106, 58]))), (64, Rule::Or(Rule::Seq([29, 63]), Rule::Seq([106, 89]))), (112, Rule::Or(Rule::Seq([106, 98]), Rule::Seq([29, 60]))), diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index cb820d1b5..f35fcf020 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -13,6 +13,7 @@ - [Template literals](./template_literals.md) - [Instance functions](./instance_functions.md) - [Field functions](./field_functions.md) + - [Traits](./traits.md) - [Built-in types](./types.md) - [Primitives and references](./primitives.md) - [Vectors](./vectors.md) diff --git a/book/src/traits.md b/book/src/traits.md new file mode 100644 index 000000000..ea0180583 --- /dev/null +++ b/book/src/traits.md @@ -0,0 +1,144 @@ +# Traits + +Traits in rune defines a collection associated items. Once a trait is +implemented by a type we can be sure that all the associated names it defines +are present on the type. + +Traits allow us to reason about types more abstractly, such as this is an +iterator. + +#### Limits + +As usual, Rune doesn't permit more than one definition of an associated name. +Attempting to define more than one with the same name results in a build-time +error. This is in contrast to Rust which allows multiple traits with overlapping +methods to be defined. So why doesn't Rune allow for this? + +Since Rune is a dynamic language, consider what would happen in a situation like +this: + +```rust +struct Foo { + /* .. */ +} + +impl Iterator for Foo { + fn next(self) { + /* .. */ + } +} + +impl OtherIterator for Foo { + fn next(self) { + /* .. */ + } +} + +let foo = Foo { + /* .. */ +}; + +// Which implementation of `next` should we call? +while let Some(value) = foo.next() { + +} +``` + +Since there are no type parameters we can't solve the ambiguity by either only +having one trait defining the method in scope or by using an unambigious +function qualified function call. + +#### Implementation + +In the background the user-facing implementation of traits is done by +implementing protocols just before. Protocols are still used by the virtual +machine to call functions. + +The separation that protocols provide is important because we don't want a user +to *accidentally* implement an associated method which would then be picked up +by a trait. Protocols are uniquely defined in their own namespace and cannot be +invoked in user code. + +As an example, to implement the `Iterator` trait you have to implement the +`NEXT` protocol. So if the `NEXT` protocol is present and we request that the +`::std::iter::Iterator` trait should be implemented, the `NEXT` protocol +implementation is used to construct all the relevant associated methods. This is +done by calling `Module::implement_trait`. + +```rust +let mut m = Module::with_item(["module"]); +m.ty::()?; +m.function_meta(Iter::next__meta)?; +m.function_meta(Iter::size_hint__meta)?; +m.implement_trait::(rune::item!(::std::iter::Iterator))?; + +#[derive(Any)] +#[rune(item = "module")] +struct Iter { + /* .. */ +} + +impl Iter { + #[rune::function(keep, protocol = NEXT)] + fn size_hint(&self) -> Option { + Some(true) + } + + #[rune::function(keep, protocol = SIZE_HINT)] + fn size_hint(&self) -> (usize, Option) { + (1, None) + } +} +``` + +Note that this allows the `Iter` type above to specialize its `SIZE_HINT` +implementation. If the `SIZE_HINT` protocol was not defined, a default +implementation would be provided by the trait. + +As a result of implementing the `::std::iter::Iterator` trait, the `Iter` type +now *automatically* gets all the iterator-associated function added to it. So +not only can you call `Iter::next` to advance the iterator, but also make use of +combinators such as `filter`: + +```rust +let it = /* construct Iter */; + +for value in it.filter(|v| v != true) { + dbg!(value); +} +``` + +#### Defining a trait + +Defining a trait is currently a low-level module operation. It's done by +implementing a handler which will be called to populate the relevant methods +when the trait is implement. Such as this snippet for the `Iterator` trait: + +```rust +let mut m = Module::with_crate("std", ["iter"]); + +let mut t = m.define_trait(["Iterator"])?; + +t.handler(|cx| { + let next = cx.find(Protocol::NEXT)?; + cx.function_handler("next", &next)?; + + let size_hint = if let Some(size_hint) = cx.try_find(Protocol::SIZE_HINT)? { + cx.function_handler("size_hint", &size_hint)?; + size_hint + } else { + let size_hint = cx.function("size_hint", |_: Value| (0usize, None::))?; + cx.function_handler(Protocol::SIZE_HINT, &size_hint)?; + size_hint + }; + + /* more methods */ + Ok(()) +})?; +``` + +Calling `find` requires that `NEXT` is implemented. We can also see that the +implementation for `SIZE_HINT` will fall back to a default implementation if +it's not implemented. The appropriate protocol is also populated if it's +missing. All the relevant associated functions are also provided, such as +`value.next()` and `value.size_hint()`. diff --git a/crates/rune-alloc/src/btree/node.rs b/crates/rune-alloc/src/btree/node.rs index a682a11ef..c4e35d577 100644 --- a/crates/rune-alloc/src/btree/node.rs +++ b/crates/rune-alloc/src/btree/node.rs @@ -141,6 +141,8 @@ type BoxedNode = NonNull>; /// /// This type has a number of parameters that controls how it acts: /// - `BorrowType`: A dummy type that describes the kind of borrow and carries a lifetime. +/// Since any `NodeRef` allows navigating through the tree, `BorrowType` +/// effectively applies to the entire tree, not just to the node itself. /// - When this is `Immut<'a>`, the `NodeRef` acts roughly like `&'a Node`. /// - When this is `ValMut<'a>`, the `NodeRef` acts roughly like `&'a Node` /// with respect to keys and tree structure, but also allows many @@ -152,8 +154,6 @@ type BoxedNode = NonNull>; /// - When this is `Dying`, the `NodeRef` still acts roughly like `Box`, /// but has methods to destroy the tree bit by bit, and ordinary methods, /// while not marked as unsafe to call, can invoke UB if called incorrectly. -/// Since any `NodeRef` allows navigating through the tree, `BorrowType` -/// effectively applies to the entire tree, not just to the node itself. /// - `K` and `V`: These are the types of keys and values stored in the nodes. /// - `Type`: This can be `Leaf`, `Internal`, or `LeafOrInternal`. When this is /// `Leaf`, the `NodeRef` points to a leaf node, when this is `Internal` the diff --git a/crates/rune-alloc/src/lib.rs b/crates/rune-alloc/src/lib.rs index 61cd4f2d0..a9fa695fd 100644 --- a/crates/rune-alloc/src/lib.rs +++ b/crates/rune-alloc/src/lib.rs @@ -15,9 +15,10 @@ //!
//! //! The Rune Language, an embeddable dynamic programming language for Rust. + // Quite a few parts copied from the Rust Project under the MIT license. // -// Copyright 2014-2023 The Rust Project Developers +// Copyright 2014-2024 The Rust Project Developers // // Licensed under the Apache License, Version 2.0 or the MIT license Self { - if !ty.is_empty() { - Self(self.0 ^ (ty.0 ^ TYPE_PARAMETERS)) - } else { - self - } + Self(self.0 ^ ty.0.wrapping_shl(TYPE_PARAMETERS)) } /// Mix the current hash with function parameters. pub const fn with_function_parameters(self, f: Self) -> Self { - if !f.is_empty() { - Self(self.0 ^ (f.0 ^ FUNCTION_PARAMETERS)) - } else { - self - } + Self(self.0 ^ f.0.wrapping_shl(FUNCTION_PARAMETERS)) } /// Hash type parameters. diff --git a/crates/rune-core/src/item/item.rs b/crates/rune-core/src/item/item.rs index 53b48be5e..92c419354 100644 --- a/crates/rune-core/src/item/item.rs +++ b/crates/rune-core/src/item/item.rs @@ -166,11 +166,7 @@ impl Item { /// assert_eq!(item2.last(), Some(ComponentRef::Str("world"))); /// # Ok::<(), rune::support::Error>(()) /// ``` - pub fn join(&self, other: I) -> alloc::Result - where - I: IntoIterator, - I::Item: IntoComponent, - { + pub fn join(&self, other: impl IntoIterator) -> alloc::Result { let mut content = self.content.try_to_owned()?; for c in other { diff --git a/crates/rune-core/src/item/item_buf.rs b/crates/rune-core/src/item/item_buf.rs index 2cbd1c648..02c2ef4aa 100644 --- a/crates/rune-core/src/item/item_buf.rs +++ b/crates/rune-core/src/item/item_buf.rs @@ -85,11 +85,10 @@ impl ItemBuf { } /// Construct a new item with the given path in the given allocator. - pub(crate) fn with_item_in(iter: I, alloc: A) -> alloc::Result - where - I: IntoIterator, - I::Item: IntoComponent, - { + pub(crate) fn with_item_in( + iter: impl IntoIterator, + alloc: A, + ) -> alloc::Result { let mut content = Vec::new_in(alloc); for c in iter { @@ -184,11 +183,7 @@ impl ItemBuf { /// assert_eq!(it.next(), None); /// # Ok::<(), rune::support::Error>(()) /// ``` - pub fn with_item(iter: I) -> alloc::Result - where - I: IntoIterator, - I::Item: IntoComponent, - { + pub fn with_item(iter: impl IntoIterator) -> alloc::Result { Self::with_item_in(iter, Global) } diff --git a/crates/rune-core/src/protocol.rs b/crates/rune-core/src/protocol.rs index 73d3f409a..e18140e96 100644 --- a/crates/rune-core/src/protocol.rs +++ b/crates/rune-core/src/protocol.rs @@ -441,6 +441,46 @@ define! { doc: ["Allows iteration to be advanced for the type, this is used for iterators."], }; + /// The function to call to continue iteration at the nth element. + pub const [NTH, NTH_HASH]: Protocol = Protocol { + name: "nth", + hash: 0x6704550736c82a58u64, + repr: None, + doc: ["Allows iteration to be advanced for the type to the nth element, this is used for iterators."], + }; + + /// The function to call to continue iteration at the nth element form the back. + pub const [NTH_BACK, NTH_BACK_HASH]: Protocol = Protocol { + name: "nth_back", + hash: 0x4885ca2fd53a08c8u64, + repr: None, + doc: ["Allows iteration to be advanced for the type to the nth element from the back, this is used for iterators."], + }; + + /// Protocol used when getting the size hint of an iterator. + pub const [SIZE_HINT, SIZE_HINT_HASH]: Protocol = Protocol { + name: "size_hint", + hash: 0x1a7b50baabc6e094u64, + repr: Some("let output = $value.size_hint()"), + doc: ["Get the size hint of an iterator."], + }; + + /// Protocol used when getting the exact length of an iterator. + pub const [LEN, LEN_HASH]: Protocol = Protocol { + name: "len", + hash: 0x52dd3b9489d39c42u64, + repr: Some("let output = $value.len()"), + doc: ["Get the length of an iterator."], + }; + + /// Protocol used when cloning a value. + pub const [NEXT_BACK, NEXT_BACK_HASH]: Protocol = Protocol { + name: "next_back", + hash: 0x91149fef42c0a8aeu64, + repr: Some("let output = $value.next_back()"), + doc: ["Get the next value from the back of the iterator."], + }; + /// Function used to convert an argument into a future. /// /// Signature: `fn(Value) -> Future`. @@ -492,7 +532,7 @@ define! { name: "hash", hash: 0xf6cf2d9f416cef08u64, repr: Some("let output = hash($value)"), - doc: ["Hash the given value."], + doc: ["Hash a value."], }; /// Protocol used when cloning a value. @@ -500,22 +540,6 @@ define! { name: "clone", hash: 0x2af2c875e36971eu64, repr: Some("let output = clone($value)"), - doc: ["Clone the given value."], - }; - - /// Protocol used when cloning a value. - pub const [SIZE_HINT, SIZE_HINT_HASH]: Protocol = Protocol { - name: "size_hint", - hash: 0x3de0975a7000dau64, - repr: Some("let output = $value.size_hint()"), - doc: ["Get the size hint of the given iterator."], - }; - - /// Protocol used when cloning a value. - pub const [NEXT_BACK, NEXT_BACK_HASH]: Protocol = Protocol { - name: "next_back", - hash: 0x91149fef42c0a8aeu64, - repr: Some("let output = $value.next_back()"), - doc: ["Get the next value from the back of the iterator."], + doc: ["Clone a value."], }; } diff --git a/crates/rune-macros/src/any.rs b/crates/rune-macros/src/any.rs index 55470513f..47fcdfec1 100644 --- a/crates/rune-macros/src/any.rs +++ b/crates/rune-macros/src/any.rs @@ -44,8 +44,9 @@ impl InternalCall { let mut item = self.item.clone(); item.segments.push(syn::PathSegment::from(name.clone())); + let args = crate::hash::Arguments::new(item); - let type_hash = match crate::hash::build_type_hash(&item) { + let type_hash = match crate::hash::build_type_hash(&args) { Ok(type_hash) => type_hash, Err(error) => { cx.error(error); @@ -105,8 +106,9 @@ impl Derive { }; item.segments.push(syn::PathSegment::from(name.clone())); + let args = crate::hash::Arguments::new(item); - let type_hash = match crate::hash::build_type_hash(&item) { + let type_hash = match crate::hash::build_type_hash(&args) { Ok(type_hash) => type_hash, Err(error) => { cx.error(error); @@ -506,6 +508,7 @@ where any, box_, context_error, + core_type_of, from_value, hash, install_with, @@ -573,20 +576,31 @@ where }; let impl_type_of = if attr.builtin.is_none() { + let type_hash = type_hash.into_inner(); + + let make_hash = if !generic_names.is_empty() { + quote!(#hash::new_with_type_parameters(#type_hash, #hash::parameters([#(<#generic_names as #core_type_of>::type_hash()),*]))) + } else { + quote!(#hash::new(#type_hash)) + }; + let type_parameters = if !generic_names.is_empty() { - quote!(#hash::parameters([#(<#generic_names as #type_of>::type_hash()),*])) + quote!(#hash::parameters([#(<#generic_names as #core_type_of>::type_hash()),*])) } else { quote!(#hash::EMPTY) }; Some(quote! { #[automatically_derived] - impl #impl_generics #type_of for #ident #type_generics #where_clause { + impl #impl_generics #core_type_of for #ident #type_generics #where_clause { #[inline] fn type_hash() -> #hash { - ::type_hash() + #make_hash } + } + #[automatically_derived] + impl #impl_generics #type_of for #ident #type_generics #where_clause { #[inline] fn type_parameters() -> #hash { #type_parameters @@ -596,7 +610,7 @@ where fn type_info() -> #type_info { #type_info::Any(#any_type_info::__private_new( #raw_str::from_str(core::any::type_name::()), - ::type_hash(), + ::type_hash(), )) } } @@ -606,7 +620,7 @@ where #[inline] fn maybe_type_of() -> #alloc::Result<#meta::DocType> { #meta::DocType::with_generics( - ::type_hash(), + ::type_hash(), [#(<#generic_names as #maybe_type_of>::maybe_type_of()?),*] ) } @@ -615,12 +629,15 @@ where } else if let Some(ty) = attr.static_type { Some(quote! { #[automatically_derived] - impl #impl_generics #type_of for #ident #type_generics #where_clause { + impl #impl_generics #core_type_of for #ident #type_generics #where_clause { #[inline] fn type_hash() -> #hash { #static_type_mod::#ty.hash } + } + #[automatically_derived] + impl #impl_generics #type_of for #ident #type_generics #where_clause { #[inline] fn type_info() -> #type_info { #type_info::StaticType(#static_type_mod::#ty) @@ -632,7 +649,7 @@ where #[inline] fn maybe_type_of() -> #alloc::Result<#meta::DocType> { #meta::DocType::with_generics( - ::type_hash(), + ::type_hash(), [#(<#generic_names as #maybe_type_of>::maybe_type_of()),*] ) } @@ -643,20 +660,9 @@ where }; let any = if attr.builtin.is_none() { - let type_hash = type_hash.into_inner(); - - let make_hash = if !generic_names.is_empty() { - quote!(#hash::new_with_type_parameters(#type_hash, #hash::parameters([#(<#generic_names as #type_of>::type_hash()),*]))) - } else { - quote!(#hash::new(#type_hash)) - }; - Some(quote! { #[automatically_derived] impl #impl_generics #any for #ident #type_generics #where_clause { - fn type_hash() -> #hash { - #make_hash - } } #[automatically_derived] diff --git a/crates/rune-macros/src/context.rs b/crates/rune-macros/src/context.rs index 03cf44d87..daa32a248 100644 --- a/crates/rune-macros/src/context.rs +++ b/crates/rune-macros/src/context.rs @@ -618,6 +618,7 @@ impl Context { box_: path(m, ["__private", "Box"]), compile_error: path(m, ["compile", "Error"]), context_error: path(m, ["compile", "ContextError"]), + core_type_of: path(m, ["runtime", "CoreTypeOf"]), double_ended_iterator: path(&core, ["iter", "DoubleEndedIterator"]), from_value: path(m, ["runtime", "FromValue"]), hash: path(m, ["Hash"]), @@ -710,6 +711,7 @@ pub(crate) struct Tokens { pub(crate) box_: syn::Path, pub(crate) compile_error: syn::Path, pub(crate) context_error: syn::Path, + pub(crate) core_type_of: syn::Path, pub(crate) double_ended_iterator: syn::Path, pub(crate) from_value: syn::Path, pub(crate) hash: syn::Path, diff --git a/crates/rune-macros/src/function.rs b/crates/rune-macros/src/function.rs index 38a8818e7..a839a31e4 100644 --- a/crates/rune-macros/src/function.rs +++ b/crates/rune-macros/src/function.rs @@ -26,6 +26,8 @@ pub(crate) struct FunctionAttrs { self_type: Option, /// Defines a fallible function which can make use of the `?` operator. vm_result: bool, + /// The function is deprecated. + deprecated: Option, } impl FunctionAttrs { @@ -96,6 +98,9 @@ impl FunctionAttrs { } else { out.path = Path::Rename(first); } + } else if ident == "deprecated" { + input.parse::()?; + out.deprecated = Some(input.parse()?); } else { return Err(syn::Error::new_spanned(ident, "Unsupported option")); } @@ -226,7 +231,7 @@ impl Function { } } - let real_fn_path = syn::TypePath { qself: None, path }; + let real_fn_path = path; let name_string = syn::LitStr::new(&self.sig.ident.to_string(), self.sig.ident.span()); @@ -284,7 +289,7 @@ impl Function { for argument in arguments { array.elems.push(syn::Expr::Verbatim(quote! { - <#argument as rune::__private::TypeOf>::type_hash() + <#argument as rune::__private::CoreTypeOf>::type_hash() })); } @@ -340,6 +345,7 @@ impl Function { }; } + let generics = sig.generics.clone(); stream.extend(sig.into_token_stream()); if attrs.vm_result { @@ -361,19 +367,29 @@ impl Function { Some(quote!(.build()?)) }; - let attr = (!real_fn_mangled).then(|| quote!(#[allow(non_snake_case)] #[doc(hidden)])); + let attributes = (!real_fn_mangled).then(|| quote!(#[allow(non_snake_case)])); + + let deprecated = match &attrs.deprecated { + Some(message) => quote!(Some(#message)), + None => quote!(None), + }; + + let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); + let type_generics = type_generics.as_turbofish(); stream.extend(quote! { /// Get function metadata. #[automatically_derived] - #attr + #attributes #[doc(hidden)] - pub(crate) fn #meta_fn() -> rune::alloc::Result { + pub(crate) fn #meta_fn #impl_generics() -> rune::alloc::Result + #where_clause + { Ok(rune::__private::FunctionMetaData { - kind: rune::__private::FunctionMetaKind::#meta_kind(#name, #real_fn_path)?#build_with, + kind: rune::__private::FunctionMetaKind::#meta_kind(#name, #real_fn_path #type_generics)?#build_with, statics: rune::__private::FunctionMetaStatics { name: #name_string, - deprecated: None, + deprecated: #deprecated, docs: &#docs[..], arguments: &#arguments[..], }, diff --git a/crates/rune-macros/src/hash.rs b/crates/rune-macros/src/hash.rs index 2ff12516d..d1ff7b26a 100644 --- a/crates/rune-macros/src/hash.rs +++ b/crates/rune-macros/src/hash.rs @@ -2,14 +2,46 @@ use core::mem::take; use rune_core::hash::Hash; use rune_core::item::{ComponentRef, ItemBuf}; +use syn::parse::{Parse, ParseStream}; + +pub(super) struct Arguments { + path: syn::Path, + associated: Option<(syn::Token![.], syn::Ident)>, +} + +impl Arguments { + pub(super) fn new(path: syn::Path) -> Self { + Self { + path, + associated: None, + } + } +} + +impl Parse for Arguments { + fn parse(input: ParseStream) -> syn::Result { + let path = input.parse()?; + + let ident = if let Some(colon) = input.parse::>()? { + Some((colon, input.parse()?)) + } else { + None + }; + + Ok(Self { + path, + associated: ident, + }) + } +} /// Construct a type hash from a Rust path. -pub(crate) fn build_type_hash(path: &syn::Path) -> syn::Result { +pub(crate) fn build_type_hash(args: &Arguments) -> syn::Result { // Construct type hash. let mut buf = ItemBuf::new(); - let mut first = path.leading_colon.is_some(); + let mut first = args.path.leading_colon.is_some(); - for s in &path.segments { + for s in &args.path.segments { let ident = s.ident.to_string(); let c = if take(&mut first) { @@ -38,5 +70,14 @@ pub(crate) fn build_type_hash(path: &syn::Path) -> syn::Result { } } - Ok(Hash::type_hash(&buf)) + let base = Hash::type_hash(&buf); + + let hash = if let Some((_, associated)) = &args.associated { + let name = associated.to_string(); + Hash::associated_function(base, name.as_str()) + } else { + base + }; + + Ok(hash) } diff --git a/crates/rune-macros/src/lib.rs b/crates/rune-macros/src/lib.rs index d669cdd35..699c3556b 100644 --- a/crates/rune-macros/src/lib.rs +++ b/crates/rune-macros/src/lib.rs @@ -199,16 +199,16 @@ pub fn any(input: proc_macro::TokenStream) -> proc_macro::TokenStream { /// ``` /// use rune::Hash; /// -/// let hash: Hash = rune::hash!(::std::ops::Generator); +/// let hash: Hash = rune::hash!(::std::ops::generator::Generator); /// ``` #[proc_macro] pub fn hash(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let path = syn::parse_macro_input!(input as syn::Path); + let args = syn::parse_macro_input!(input as self::hash::Arguments); - let stream = match self::hash::build_type_hash(&path) { + let stream = match self::hash::build_type_hash(&args) { Ok(hash) => { let hash = hash.into_inner(); - ::quote::quote!(rune::Hash::new(#hash)) + ::quote::quote!(rune::Hash(#hash)) } Err(error) => to_compile_errors([error]), }; @@ -223,10 +223,11 @@ pub fn hash(input: proc_macro::TokenStream) -> proc_macro::TokenStream { /// ``` /// use rune::{Item, ItemBuf}; /// -/// static ITEM: &Item = rune::item!(::std::ops::Generator); +/// static ITEM: &Item = rune::item!(::std::ops::generator::Generator); /// /// let mut item = ItemBuf::with_crate("std")?; /// item.push("ops")?; +/// item.push("generator")?; /// item.push("Generator")?; /// /// assert_eq!(item, ITEM); diff --git a/crates/rune/Cargo.toml b/crates/rune/Cargo.toml index 9956f0c42..01a7e98d3 100644 --- a/crates/rune/Cargo.toml +++ b/crates/rune/Cargo.toml @@ -73,6 +73,7 @@ similar = { version = "2.2.1", optional = true, features = ["inline", "bytes"] } sha2 = { version = "0.10.6", optional = true } base64 = { version = "0.21.0", optional = true } rand = { version = "0.8.5", optional = true } +memchr = "2.7.4" [dev-dependencies] tokio = { version = "1.28.1", features = ["full"] } diff --git a/crates/rune/src/any.rs b/crates/rune/src/any.rs index 0224d2cc3..350a90f48 100644 --- a/crates/rune/src/any.rs +++ b/crates/rune/src/any.rs @@ -1,7 +1,7 @@ use core::any; use crate::compile::Named; -use crate::hash::Hash; +use crate::runtime::CoreTypeOf; /// Macro to mark a value as external, which will implement all the appropriate /// traits. @@ -72,7 +72,7 @@ pub use rune_macros::Any; /// [`Context::install`]: crate::Context::install /// [`Module`]: crate::Module /// [`String`]: std::string::String -/// [`#[rune::function]`]: crate::function +/// [`#[rune::function]`]: macro@crate::function /// [`#[rune::macro_]`]: crate::macro_ /// /// # Examples @@ -110,10 +110,7 @@ pub use rune_macros::Any; /// Ok(module) /// } /// ``` -pub trait Any: Named + any::Any { - /// The type hash of the type. - fn type_hash() -> Hash; -} +pub trait Any: CoreTypeOf + Named + any::Any {} // Internal any impls for useful types in the std library. diff --git a/crates/rune/src/cli/tests.rs b/crates/rune/src/cli/tests.rs index 13dbc5564..4fe5200bc 100644 --- a/crates/rune/src/cli/tests.rs +++ b/crates/rune/src/cli/tests.rs @@ -277,10 +277,9 @@ where continue; } - executed = executed.wrapping_add(1); - let mut vm = Vm::new(runtime.clone(), case.unit.clone()); case.execute(&mut vm, &capture).await?; + executed = executed.wrapping_add(1); if case.outcome.is_ok() { if flags.quiet { diff --git a/crates/rune/src/compile/context.rs b/crates/rune/src/compile/context.rs index 2c580b431..676e2c595 100644 --- a/crates/rune/src/compile/context.rs +++ b/crates/rune/src/compile/context.rs @@ -5,24 +5,182 @@ use ::rust_alloc::sync::Arc; use crate as rune; use crate::alloc::prelude::*; use crate::alloc::{self, BTreeSet, Box, HashMap, HashSet, String, Vec}; -use crate::compile::meta; -#[cfg(feature = "doc")] -use crate::compile::Docs; #[cfg(feature = "emit")] use crate::compile::MetaInfo; -use crate::compile::{ContextError, Names}; +use crate::compile::{self, ContextError, Names}; +use crate::compile::{meta, Docs}; +use crate::function::{Function, Plain}; +use crate::function_meta::{AssociatedName, ToInstance}; use crate::hash; use crate::item::{ComponentRef, IntoComponent}; +use crate::macros::{MacroContext, TokenStream}; use crate::module::{ - Fields, Module, ModuleAssociated, ModuleAssociatedKind, ModuleItem, ModuleType, + DocFunction, Fields, Module, ModuleAssociated, ModuleAssociatedKind, ModuleFunction, + ModuleItem, ModuleItemCommon, ModuleReexport, ModuleTrait, ModuleTraitImpl, ModuleType, TypeSpecification, }; use crate::runtime::{ - AttributeMacroHandler, ConstValue, FunctionHandler, MacroHandler, Protocol, RuntimeContext, - StaticType, TypeCheck, TypeInfo, VariantRtti, + ConstValue, FunctionHandler, InstAddress, Memory, Output, Protocol, RuntimeContext, StaticType, + TypeCheck, TypeInfo, VariantRtti, VmResult, }; use crate::{Hash, Item, ItemBuf}; +/// A (type erased) macro handler. +pub(crate) type MacroHandler = + dyn Fn(&mut MacroContext, &TokenStream) -> compile::Result + Send + Sync; + +/// Invoked when types implement a trait. +pub(crate) type TraitHandler = + dyn Fn(&mut TraitContext<'_>) -> Result<(), ContextError> + Send + Sync; + +/// A (type erased) attribute macro handler. +pub(crate) type AttributeMacroHandler = dyn Fn(&mut MacroContext, &TokenStream, &TokenStream) -> compile::Result + + Send + + Sync; + +/// Type used to install traits. +pub struct TraitContext<'a> { + /// The context the trait function are being installed into. + cx: &'a mut Context, + /// The item being installed. + item: &'a Item, + /// The hash of the item being installed. + hash: Hash, + /// Type info of the type being installed. + type_info: &'a TypeInfo, + /// The trait being implemented for. + trait_item: &'a Item, + /// Hash of the trait being impleemnted. + trait_hash: Hash, +} + +impl TraitContext<'_> { + /// Return the item the trait is being installed for. + pub fn item(&self) -> &Item { + self.item + } + + /// Return the hash the trait is being installed for. + pub fn hash(&self) -> Hash { + self.hash + } + + /// Find the given protocol function for the current type. + /// + /// This requires that the function is defined. + pub fn find(&self, name: impl ToInstance) -> Result, ContextError> { + let name = name.to_instance()?; + + let hash = name + .kind + .hash(self.hash) + .with_function_parameters(name.function_parameters); + + let Some(handler) = self.cx.functions.get(&hash) else { + return Err(ContextError::MissingTraitFunction { + name: name.kind.try_to_string()?, + item: self.item.try_to_owned()?, + hash, + trait_item: self.trait_item.try_to_owned()?, + trait_hash: self.trait_hash, + }); + }; + + Ok(handler.clone()) + } + + /// Try to find the given associated function. + /// + /// This does not require that the function is defined. + pub fn try_find( + &self, + name: impl ToInstance, + ) -> Result>, ContextError> { + let name = name.to_instance()?; + + let hash = name + .kind + .hash(self.hash) + .with_function_parameters(name.function_parameters); + + Ok(self.cx.functions.get(&hash).cloned()) + } + + /// Define a new associated function for the current type. + pub fn function( + &mut self, + name: impl ToInstance, + handler: F, + ) -> Result, ContextError> + where + F: Function, + { + self.raw_function(name, move |memory, addr, len, out| { + handler.fn_call(memory, addr, len, out) + }) + } + + /// Define a new associated raw function for the current type. + pub fn raw_function( + &mut self, + name: impl ToInstance, + handler: F, + ) -> Result, ContextError> + where + F: 'static + Fn(&mut dyn Memory, InstAddress, usize, Output) -> VmResult<()> + Send + Sync, + { + let handler: Arc = Arc::new(handler); + self.function_handler(name, &handler)?; + Ok(handler) + } + + /// Define a new associated function for the current type using a raw + /// handler. + pub fn function_handler( + &mut self, + name: impl ToInstance, + handler: &Arc, + ) -> Result<(), ContextError> { + let name = name.to_instance()?; + self.function_inner(name, handler) + } + + fn function_inner( + &mut self, + name: AssociatedName, + handler: &Arc, + ) -> Result<(), ContextError> { + let function = ModuleFunction { + handler: handler.clone(), + trait_hash: Some(self.trait_hash), + doc: DocFunction { + #[cfg(feature = "doc")] + is_async: false, + #[cfg(feature = "doc")] + args: None, + #[cfg(feature = "doc")] + argument_types: Box::default(), + #[cfg(feature = "doc")] + return_type: meta::DocType::empty(), + }, + }; + + let assoc = ModuleAssociated { + container: self.hash, + container_type_info: self.type_info.try_clone()?, + name, + common: ModuleItemCommon { + docs: Docs::EMPTY, + deprecated: None, + }, + kind: ModuleAssociatedKind::Function(function), + }; + + self.cx.install_associated(&assoc)?; + Ok(()) + } +} + /// Context metadata. #[derive(Debug)] #[non_exhaustive] @@ -91,7 +249,7 @@ pub struct Context { /// Registered metadata, in the order that it was registered. meta: Vec, /// Item metadata in the context. - hash_to_meta: HashMap>, + hash_to_meta: hash::Map>, /// Store item to hash mapping. item_to_hash: HashMap>, /// Registered native function handlers. @@ -100,13 +258,18 @@ pub struct Context { deprecations: hash::Map, /// Information on associated types. #[cfg(feature = "doc")] - associated: HashMap>, + associated: hash::Map>, + /// Traits implemented by the given hash. + #[cfg(feature = "doc")] + implemented_traits: hash::Map>, /// Registered native macro handlers. - macros: HashMap>, + macros: hash::Map>, + /// Handlers for realising traits. + traits: hash::Map>>, /// Registered native attribute macro handlers. - attribute_macros: HashMap>, + attribute_macros: hash::Map>, /// Registered types. - types: HashMap, + types: hash::Map, /// Registered internal enums. internal_enums: HashSet<&'static StaticType>, /// All available names in the context. @@ -139,6 +302,8 @@ impl Context { /// * `::std::io::println` pub fn with_config(#[allow(unused)] stdio: bool) -> Result { let mut this = Self::new(); + + this.install(crate::modules::iter::module()?)?; // This must go first, because it includes types which are used in other modules. this.install(crate::modules::core::module()?)?; @@ -150,24 +315,31 @@ impl Context { this.install(crate::modules::hash::module()?)?; this.install(crate::modules::cmp::module()?)?; this.install(crate::modules::collections::module()?)?; + #[cfg(feature = "alloc")] + this.install(crate::modules::collections::hash_map::module()?)?; + #[cfg(feature = "alloc")] + this.install(crate::modules::collections::hash_set::module()?)?; + #[cfg(feature = "alloc")] + this.install(crate::modules::collections::vec_deque::module()?)?; this.install(crate::modules::f64::module()?)?; this.install(crate::modules::tuple::module()?)?; this.install(crate::modules::fmt::module()?)?; this.install(crate::modules::future::module()?)?; this.install(crate::modules::i64::module()?)?; this.install(crate::modules::io::module(stdio)?)?; - this.install(crate::modules::iter::module()?)?; this.install(crate::modules::macros::module()?)?; this.install(crate::modules::macros::builtin::module()?)?; this.install(crate::modules::mem::module()?)?; this.install(crate::modules::object::module()?)?; this.install(crate::modules::ops::module()?)?; + this.install(crate::modules::ops::generator::module()?)?; this.install(crate::modules::option::module()?)?; this.install(crate::modules::result::module()?)?; this.install(crate::modules::stream::module()?)?; this.install(crate::modules::string::module()?)?; this.install(crate::modules::test::module()?)?; this.install(crate::modules::vec::module()?)?; + this.install(crate::modules::slice::module()?)?; this.has_default_modules = true; Ok(this) } @@ -228,6 +400,10 @@ impl Context { self.install_type(module, ty)?; } + for t in &module.traits { + self.install_trait(t)?; + } + for item in &module.items { self.install_item(module, item)?; } @@ -236,6 +412,14 @@ impl Context { self.install_associated(assoc)?; } + for t in &module.trait_impls { + self.install_trait_impl(module, t)?; + } + + for r in &module.reexports { + self.install_reexport(r)?; + } + Ok(()) } @@ -333,6 +517,17 @@ impl Context { .copied() } + /// Get all traits implemented for the given hash. + #[cfg(feature = "doc")] + pub(crate) fn traits(&self, hash: Hash) -> impl Iterator + '_ { + self.implemented_traits + .get(&hash) + .map(Vec::as_slice) + .unwrap_or_default() + .iter() + .copied() + } + /// Lookup the given macro handler. pub(crate) fn lookup_macro(&self, hash: Hash) -> Option<&Arc> { self.macros.get(&hash) @@ -368,6 +563,23 @@ impl Context { self.has_default_modules } + /// Try to find an existing module. + fn find_existing_module(&self, hash: Hash) -> Option { + let indexes = self.hash_to_meta.get(&hash)?; + + for &index in indexes { + let Some(m) = self.meta.get(index) else { + continue; + }; + + if matches!(m.kind, meta::Kind::Module) { + return Some(index); + } + } + + None + } + /// Install the given meta. fn install_meta(&mut self, meta: ContextMeta) -> Result<(), ContextError> { if let Some(item) = &meta.item { @@ -386,12 +598,16 @@ impl Context { } let hash = meta.hash; + let index = self.meta.len(); + self.meta.try_push(meta)?; + self.hash_to_meta .entry(hash) .or_try_default()? .try_push(index)?; + Ok(()) } @@ -403,21 +619,32 @@ impl Context { #[allow(unused)] while let Some((item, common)) = current.take() { - self.install_meta(ContextMeta { - hash: Hash::type_hash(item), - item: Some(item.try_to_owned()?), - kind: meta::Kind::Module, - #[cfg(feature = "doc")] - deprecated: common - .map(|c| c.deprecated.as_ref().try_cloned()) - .transpose()? - .flatten(), + let hash = Hash::type_hash(item); + + if let Some(index) = self.find_existing_module(hash) { #[cfg(feature = "doc")] - docs: common - .map(|c| c.docs.try_clone()) - .transpose()? - .unwrap_or_default(), - })?; + if let Some(common) = common { + let meta = &mut self.meta[index]; + meta.deprecated = common.deprecated.try_clone()?; + meta.docs = common.docs.try_clone()?; + } + } else { + self.install_meta(ContextMeta { + hash, + item: Some(item.try_to_owned()?), + kind: meta::Kind::Module, + #[cfg(feature = "doc")] + deprecated: common + .map(|c| c.deprecated.as_ref().try_cloned()) + .transpose()? + .flatten(), + #[cfg(feature = "doc")] + docs: common + .map(|c| c.docs.try_clone()) + .transpose()? + .unwrap_or_default(), + })?; + } current = item.parent().map(|item| (item, None)); } @@ -574,6 +801,126 @@ impl Context { Ok(()) } + fn install_trait(&mut self, t: &ModuleTrait) -> Result<(), ContextError> { + if self.traits.try_insert(t.hash, t.handler.clone())?.is_some() { + return Err(ContextError::ConflictingTrait { + item: t.item.try_clone()?, + hash: t.hash, + }); + } + + self.install_meta(ContextMeta { + hash: t.hash, + item: Some(t.item.try_clone()?), + kind: meta::Kind::Trait, + #[cfg(feature = "doc")] + deprecated: t.common.deprecated.try_clone()?, + #[cfg(feature = "doc")] + docs: t.common.docs.try_clone()?, + })?; + + for f in &t.functions { + let signature = meta::Signature::from_context(&f.doc, &f.common)?; + + let kind = meta::Kind::Function { + associated: Some(f.name.kind.try_clone()?), + trait_hash: None, + signature, + is_test: false, + is_bench: false, + parameters: Hash::EMPTY.with_function_parameters(f.name.function_parameters), + #[cfg(feature = "doc")] + container: Some(t.hash), + #[cfg(feature = "doc")] + parameter_types: f.name.parameter_types.try_clone()?, + }; + + let hash = f + .name + .kind + .hash(t.hash) + .with_function_parameters(f.name.function_parameters); + + let item = if let meta::AssociatedKind::Instance(name) = &f.name.kind { + let item = t.item.extended(name.as_ref())?; + let hash = Hash::type_hash(&item); + Some((hash, item)) + } else { + None + }; + + self.install_meta(ContextMeta { + hash, + item: item.map(|(_, item)| item), + kind, + #[cfg(feature = "doc")] + deprecated: f.common.deprecated.try_clone()?, + #[cfg(feature = "doc")] + docs: f.common.docs.try_clone()?, + })?; + } + + Ok(()) + } + + fn install_trait_impl( + &mut self, + module: &Module, + i: &ModuleTraitImpl, + ) -> Result<(), ContextError> { + let item = module.item.join(&i.item)?; + + if !self.types.contains_key(&i.hash) { + return Err(ContextError::MissingType { + item, + type_info: i.type_info.try_clone()?, + }); + }; + + let Some(handler) = self.traits.get(&i.trait_hash).cloned() else { + return Err(ContextError::MissingTrait { + item: i.trait_item.try_clone()?, + hash: i.hash, + impl_item: item, + impl_hash: i.hash, + }); + }; + + if let Some(handler) = handler { + handler(&mut TraitContext { + cx: self, + item: &item, + hash: i.hash, + type_info: &i.type_info, + trait_item: &i.trait_item, + trait_hash: i.trait_hash, + })?; + } + + #[cfg(feature = "doc")] + self.implemented_traits + .entry(i.hash) + .or_try_default()? + .try_push(i.trait_hash)?; + Ok(()) + } + + fn install_reexport(&mut self, r: &ModuleReexport) -> Result<(), ContextError> { + self.install_meta(ContextMeta { + hash: r.hash, + item: Some(r.item.try_clone()?), + kind: meta::Kind::Alias(meta::Alias { + to: r.to.try_clone()?, + }), + #[cfg(feature = "doc")] + deprecated: None, + #[cfg(feature = "doc")] + docs: Docs::EMPTY, + })?; + + Ok(()) + } + fn install_type_info(&mut self, ty: ContextType) -> Result<(), ContextError> { let item_hash = Hash::type_hash(&ty.item).with_type_parameters(ty.type_parameters); @@ -624,23 +971,13 @@ impl Context { ConstValue::String(item.try_to_string()?), )?; - let signature = meta::Signature { - #[cfg(feature = "doc")] - is_async: f.is_async, - #[cfg(feature = "doc")] - arguments: context_to_arguments( - f.args, - f.argument_types.as_ref(), - module_item.common.docs.args(), - )?, - #[cfg(feature = "doc")] - return_type: f.return_type.try_clone()?, - }; + let signature = meta::Signature::from_context(&f.doc, &module_item.common)?; self.insert_native_fn(hash, &f.handler, module_item.common.deprecated.as_deref())?; meta::Kind::Function { associated: None, + trait_hash: f.trait_hash, signature, is_test: false, is_bench: false, @@ -777,8 +1114,6 @@ impl Context { }); }; - // NB: `assoc.container.hash` already contains the type hash, so it - // should not be mixed in again. let hash = assoc .name .kind @@ -813,18 +1148,7 @@ impl Context { meta::Kind::Const } ModuleAssociatedKind::Function(f) => { - let signature = meta::Signature { - #[cfg(feature = "doc")] - is_async: f.is_async, - #[cfg(feature = "doc")] - arguments: context_to_arguments( - f.args, - f.argument_types.as_ref(), - assoc.common.docs.args(), - )?, - #[cfg(feature = "doc")] - return_type: f.return_type.try_clone()?, - }; + let signature = meta::Signature::from_context(&f.doc, &assoc.common)?; if let Some((hash, item)) = &item { self.constants.try_insert( @@ -839,6 +1163,7 @@ impl Context { meta::Kind::Function { associated: Some(assoc.name.kind.try_clone()?), + trait_hash: f.trait_hash, signature, is_test: false, is_bench: false, @@ -877,9 +1202,11 @@ impl Context { } self.functions.try_insert(hash, handler.clone())?; + if let Some(msg) = deprecation { self.deprecations.try_insert(hash, msg.try_to_owned()?)?; } + Ok(()) } @@ -928,51 +1255,5 @@ fn fields_to_arguments(fields: &Fields) -> alloc::Result, - types: &[meta::DocType], - names: &[String], -) -> alloc::Result>> { - use core::iter; - - let Some(args) = args else { - return Ok(None); - }; - - let len = args.max(types.len()).max(names.len()).max(names.len()); - let mut out = Vec::try_with_capacity(len)?; - - let mut types = types.iter(); - - let names = names - .iter() - .map(|name| Some(name.as_str())) - .chain(iter::repeat(None)); - - for (n, name) in (0..len).zip(names) { - let empty; - - let ty = match types.next() { - Some(ty) => ty, - None => { - empty = meta::DocType::empty(); - &empty - } - }; - - out.try_push(meta::DocArgument { - name: match name { - Some(name) => meta::DocName::Name(Box::try_from(name)?), - None => meta::DocName::Index(n), - }, - base: ty.base, - generics: ty.generics.try_clone()?, - })?; - } - - Ok(Some(Box::try_from(out)?)) -} - #[cfg(test)] static_assertions::assert_impl_all!(Context: Send, Sync); diff --git a/crates/rune/src/compile/context_error.rs b/crates/rune/src/compile/context_error.rs index a99e8c092..e1644c00c 100644 --- a/crates/rune/src/compile/context_error.rs +++ b/crates/rune/src/compile/context_error.rs @@ -1,6 +1,7 @@ use core::fmt; -use crate::alloc::{self, Box}; +use crate::alloc; +use crate::alloc::prelude::*; use crate::runtime::{TypeInfo, VmError}; use crate::{Hash, ItemBuf}; @@ -58,6 +59,34 @@ pub enum ContextError { type_info: TypeInfo, hash: Hash, }, + ConflictingReexport { + item: ItemBuf, + hash: Hash, + to: ItemBuf, + }, + ConflictingTrait { + item: ItemBuf, + hash: Hash, + }, + ConflictingTraitImpl { + trait_item: ItemBuf, + trait_hash: Hash, + item: ItemBuf, + hash: Hash, + }, + MissingTraitFunction { + name: String, + item: ItemBuf, + hash: Hash, + trait_item: ItemBuf, + trait_hash: Hash, + }, + MissingTrait { + item: ItemBuf, + hash: Hash, + impl_item: ItemBuf, + impl_hash: Hash, + }, ConflictingTypeMeta { item: ItemBuf, type_info: TypeInfo, @@ -197,6 +226,52 @@ impl fmt::Display for ContextError { "Type `{item}` already exists `{type_info}` with hash `{hash}`" )?; } + ContextError::ConflictingReexport { item, hash, to } => { + write!( + f, + "Reexport at `{item}` with hash `{hash}` to `{to}` already exists" + )?; + } + ContextError::ConflictingTrait { item, hash } => { + write!( + f, + "Trait `{item}` with hash `{hash}` conflicts with other item in module" + )?; + } + ContextError::ConflictingTraitImpl { + trait_item, + trait_hash, + item, + hash, + } => { + write!( + f, + "Trait `{trait_item}` with hash `{trait_hash}` is implemented multiple types for type `{item}` with hash `{hash}`" + )?; + } + ContextError::MissingTraitFunction { + name, + item, + hash, + trait_item, + trait_hash, + } => { + write!( + f, + "Missing required associated `{name}` for type `{item}` with hash `{hash}` when implementing trait `{trait_item}` with hash `{trait_hash}`" + )?; + } + ContextError::MissingTrait { + item, + hash, + impl_item, + impl_hash, + } => { + write!( + f, + "Missing trait `{item}` with hash `{hash}` when implementing it for `{impl_item}` with hash `{impl_hash}`" + )?; + } ContextError::ConflictingTypeMeta { item, type_info } => { write!( f, diff --git a/crates/rune/src/compile/docs.rs b/crates/rune/src/compile/docs.rs index de528d2ff..e85a27fec 100644 --- a/crates/rune/src/compile/docs.rs +++ b/crates/rune/src/compile/docs.rs @@ -39,11 +39,10 @@ impl Docs { /// Update documentation. #[cfg(feature = "doc")] - pub(crate) fn set_docs(&mut self, docs: S) -> alloc::Result<()> - where - S: IntoIterator, - S::Item: AsRef, - { + pub(crate) fn set_docs( + &mut self, + docs: impl IntoIterator>, + ) -> alloc::Result<()> { self.docs.clear(); for line in docs { @@ -54,21 +53,16 @@ impl Docs { } #[cfg(not(feature = "doc"))] - pub(crate) fn set_docs(&mut self, _: S) -> alloc::Result<()> - where - S: IntoIterator, - S::Item: AsRef, - { + pub(crate) fn set_docs(&mut self, _: impl IntoIterator>) -> alloc::Result<()> { Ok(()) } /// Update arguments. #[cfg(feature = "doc")] - pub(crate) fn set_arguments(&mut self, arguments: S) -> alloc::Result<()> - where - S: IntoIterator, - S::Item: AsRef, - { + pub(crate) fn set_arguments( + &mut self, + arguments: impl IntoIterator>, + ) -> alloc::Result<()> { let mut out = self.arguments.take().unwrap_or_default(); out.clear(); @@ -81,11 +75,10 @@ impl Docs { } #[cfg(not(feature = "doc"))] - pub(crate) fn set_arguments(&mut self, _: S) -> alloc::Result<()> - where - S: IntoIterator, - S::Item: AsRef, - { + pub(crate) fn set_arguments( + &mut self, + _: impl IntoIterator>, + ) -> alloc::Result<()> { Ok(()) } } diff --git a/crates/rune/src/compile/error.rs b/crates/rune/src/compile/error.rs index 1d0620e52..5cac54bf8 100644 --- a/crates/rune/src/compile/error.rs +++ b/crates/rune/src/compile/error.rs @@ -21,7 +21,7 @@ use crate::runtime::{AccessError, RuntimeError, TypeInfo, TypeOf, ValueKind, VmE use crate::shared::CapacityError; #[cfg(feature = "std")] use crate::source; -use crate::{Hash, ItemBuf, SourceId}; +use crate::{Hash, Item, ItemBuf, SourceId}; /// An error raised by the compiler. #[derive(Debug)] @@ -266,7 +266,7 @@ pub(crate) enum ErrorKind { }, MissingItemParameters { item: ItemBuf, - parameters: Box<[Option]>, + parameters: [Option; 2], }, UnsupportedGlobal, UnsupportedModuleSource, @@ -619,14 +619,14 @@ impl fmt::Display for ErrorKind { write!(f, "Module `{item}` has already been loaded")?; } ErrorKind::MissingMacro { item } => { - write!(f, "Missing macro `{item}`")?; + write!(f, "Missing macro {item}")?; } ErrorKind::MissingSelf => write!(f, "No `self` in current context")?, ErrorKind::MissingLocal { name } => { write!(f, "No local variable `{name}`")?; } ErrorKind::MissingItem { item } => { - write!(f, "Missing item `{item}`")?; + write!(f, "Missing item {item}")?; } ErrorKind::MissingItemHash { hash } => { write!( @@ -635,7 +635,7 @@ impl fmt::Display for ErrorKind { )?; } ErrorKind::MissingItemParameters { item, parameters } => { - write!(f, "Missing item `{item} {parameters:?}`",)?; + write!(f, "Missing item {}", ParameterizedItem(item, parameters))?; } ErrorKind::UnsupportedGlobal => { write!(f, "Unsupported crate prefix `::`")?; @@ -1047,6 +1047,38 @@ impl fmt::Display for ErrorKind { } } +struct ParameterizedItem<'a>(&'a Item, &'a [Option; 2]); + +impl fmt::Display for ParameterizedItem<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut it = self.0.iter(); + + let (Some(item), Some(ty)) = (it.next_back(), it.next_back()) else { + return self.0.fmt(f); + }; + + let base = it.as_item(); + + let [ty_param, item_param] = self.1; + + write!(f, "{base}")?; + + if let Some(ty_param) = ty_param { + write!(f, "::{ty}<{ty_param}>")?; + } else { + write!(f, "::{ty}")?; + } + + if let Some(item_param) = item_param { + write!(f, "::{item}<{item_param}>")?; + } else { + write!(f, "::{item}")?; + } + + Ok(()) + } +} + impl From for Error { #[inline] fn from(error: alloc::Error) -> Self { diff --git a/crates/rune/src/compile/meta.rs b/crates/rune/src/compile/meta.rs index 9ce749af7..30d380c36 100644 --- a/crates/rune/src/compile/meta.rs +++ b/crates/rune/src/compile/meta.rs @@ -10,10 +10,13 @@ use crate::alloc::{self, Box, HashMap, Vec}; use crate::ast; use crate::ast::{Span, Spanned}; use crate::compile::attrs::Parser; +#[cfg(feature = "doc")] +use crate::compile::meta; use crate::compile::{self, ItemId, Location, MetaInfo, ModId, Pool, Visibility}; +use crate::module::{DocFunction, ModuleItemCommon}; use crate::parse::{NonZeroId, ResolveContext}; use crate::runtime::{Call, Protocol}; -use crate::{Hash, Item}; +use crate::{Hash, Item, ItemBuf}; /// A meta reference to an item being compiled. #[derive(Debug, TryClone, Clone, Copy)] @@ -123,10 +126,12 @@ impl Meta { Kind::Variant { .. } => None, Kind::Const { .. } => None, Kind::ConstFn { .. } => None, - Kind::Import { .. } => None, Kind::Macro => None, Kind::AttributeMacro => None, + Kind::Import { .. } => None, + Kind::Alias { .. } => None, Kind::Module => None, + Kind::Trait => None, } } } @@ -186,6 +191,8 @@ pub enum Kind { /// The associated kind of the function, if it is an associated /// function. associated: Option, + /// The hash of the trait this function is associated with. + trait_hash: Option, /// Native signature for this function. signature: Signature, /// Whether this function has a `#[test]` annotation @@ -224,8 +231,12 @@ pub enum Kind { }, /// Purely an import. Import(Import), + /// A re-export. + Alias(Alias), /// A module. Module, + /// A trait. + Trait, } impl Kind { @@ -271,10 +282,17 @@ pub struct Import { pub(crate) location: Location, /// The item being imported. pub(crate) target: ItemId, - /// The module in which the imports is located. + /// The module in which the imports are located. pub(crate) module: ModId, } +/// A context alias. +#[derive(Debug, TryClone)] +pub struct Alias { + /// The item being aliased. + pub(crate) to: ItemBuf, +} + /// Metadata about named fields. #[derive(Debug, TryClone)] #[non_exhaustive] @@ -328,6 +346,74 @@ pub struct Signature { pub(crate) return_type: DocType, } +impl Signature { + /// Construct a signature from context metadata. + #[cfg_attr(not(feature = "doc"), allow(unused_variables))] + pub(crate) fn from_context( + doc: &DocFunction, + common: &ModuleItemCommon, + ) -> alloc::Result { + Ok(Self { + #[cfg(feature = "doc")] + is_async: doc.is_async, + #[cfg(feature = "doc")] + arguments: context_to_arguments( + doc.args, + doc.argument_types.as_ref(), + common.docs.args(), + )?, + #[cfg(feature = "doc")] + return_type: doc.return_type.try_clone()?, + }) + } +} + +#[cfg(feature = "doc")] +fn context_to_arguments( + args: Option, + types: &[meta::DocType], + names: &[String], +) -> alloc::Result>> { + use core::iter; + + let Some(args) = args else { + return Ok(None); + }; + + let len = args.max(types.len()).max(names.len()).max(names.len()); + let mut out = Vec::try_with_capacity(len)?; + + let mut types = types.iter(); + + let names = names + .iter() + .map(|name| Some(name.as_str())) + .chain(iter::repeat(None)); + + for (n, name) in (0..len).zip(names) { + let empty; + + let ty = match types.next() { + Some(ty) => ty, + None => { + empty = meta::DocType::empty(); + &empty + } + }; + + out.try_push(meta::DocArgument { + name: match name { + Some(name) => meta::DocName::Name(Box::try_from(name)?), + None => meta::DocName::Index(n), + }, + base: ty.base, + generics: ty.generics.try_clone()?, + })?; + } + + Ok(Some(Box::try_from(out)?)) +} + /// A name inside of a document. #[derive(Debug, TryClone)] #[cfg(feature = "doc")] @@ -372,7 +458,7 @@ pub(crate) struct DocArgument { } /// A description of a type. -#[derive(Debug, TryClone)] +#[derive(Default, Debug, TryClone)] pub struct DocType { /// The base type. #[cfg(feature = "doc")] diff --git a/crates/rune/src/compile/meta_info.rs b/crates/rune/src/compile/meta_info.rs index 7b17aaab3..e9223e5c1 100644 --- a/crates/rune/src/compile/meta_info.rs +++ b/crates/rune/src/compile/meta_info.rs @@ -84,9 +84,15 @@ impl fmt::Display for MetaInfo { MetaInfoKind::Import => { write!(fmt, "import {name}")?; } + MetaInfoKind::Alias => { + write!(fmt, "import {name}")?; + } MetaInfoKind::Module => { write!(fmt, "module {name}")?; } + MetaInfoKind::Trait => { + write!(fmt, "trait {name}")?; + } } Ok(()) @@ -108,7 +114,9 @@ pub(crate) enum MetaInfoKind { Const, ConstFn, Import, + Alias, Module, + Trait, } impl MetaInfoKind { @@ -132,7 +140,9 @@ impl MetaInfoKind { meta::Kind::Const { .. } => MetaInfoKind::Const, meta::Kind::ConstFn { .. } => MetaInfoKind::ConstFn, meta::Kind::Import { .. } => MetaInfoKind::Import, + meta::Kind::Alias { .. } => MetaInfoKind::Alias, meta::Kind::Module { .. } => MetaInfoKind::Module, + meta::Kind::Trait { .. } => MetaInfoKind::Trait, } } } diff --git a/crates/rune/src/compile/named.rs b/crates/rune/src/compile/named.rs index 2a0a15a7d..e895af34d 100644 --- a/crates/rune/src/compile/named.rs +++ b/crates/rune/src/compile/named.rs @@ -1,6 +1,6 @@ use core::cmp::Ordering; -use crate::alloc::String; +use crate::alloc::{Box, String}; use crate::module::InstallWith; use crate::runtime::RawStr; @@ -23,6 +23,10 @@ impl Named for &str { const BASE_NAME: RawStr = RawStr::from_str("String"); } +impl Named for Box { + const BASE_NAME: RawStr = RawStr::from_str("String"); +} + impl InstallWith for String {} impl Named for i64 { diff --git a/crates/rune/src/compile/unit_builder.rs b/crates/rune/src/compile/unit_builder.rs index f13127f88..48872b113 100644 --- a/crates/rune/src/compile/unit_builder.rs +++ b/crates/rune/src/compile/unit_builder.rs @@ -625,7 +625,9 @@ impl UnitBuilder { meta::Kind::AsyncBlock { .. } => (), meta::Kind::ConstFn { .. } => (), meta::Kind::Import { .. } => (), + meta::Kind::Alias { .. } => (), meta::Kind::Module { .. } => (), + meta::Kind::Trait { .. } => (), } Ok(()) diff --git a/crates/rune/src/doc/build.rs b/crates/rune/src/doc/build.rs index 306dc5b80..4544c241b 100644 --- a/crates/rune/src/doc/build.rs +++ b/crates/rune/src/doc/build.rs @@ -1,4 +1,3 @@ -mod enum_; mod js; mod markdown; mod type_; @@ -19,7 +18,7 @@ use crate as rune; use crate::alloc::borrow::Cow; use crate::alloc::fmt::TryWrite; use crate::alloc::prelude::*; -use crate::alloc::{self, VecDeque}; +use crate::alloc::{self, HashSet, VecDeque}; use crate::compile::meta; use crate::doc::artifacts::Test; use crate::doc::context::{Function, Kind, Meta, Signature}; @@ -141,7 +140,7 @@ pub(crate) fn build( .into_iter() .find(|m| matches!(&m.kind, Kind::Module)) .with_context(|| anyhow!("Missing meta for {item}"))?; - initial.try_push_back(Build::Module(meta))?; + initial.try_push_back((Build::Module, meta))?; } let search_index = RelativePath::new("index.js"); @@ -162,7 +161,6 @@ pub(crate) fn build( type_template: compile(&templating, "type.html.hbs")?, macro_template: compile(&templating, "macro.html.hbs")?, function_template: compile(&templating, "function.html.hbs")?, - enum_template: compile(&templating, "enum.html.hbs")?, syntax_set, tests: Vec::new(), }; @@ -172,35 +170,47 @@ pub(crate) fn build( let mut modules = Vec::new(); let mut builders = Vec::new(); - while let Some(build) = queue.pop_front() { + let mut visited = HashSet::new(); + + while let Some((build, meta)) = queue.pop_front() { + if !visited.try_insert(meta.hash)? { + continue; + } + match build { - Build::Type(meta) => { + Build::Type => { cx.set_path(meta)?; let (builder, items) = self::type_::build(&mut cx, "Type", "type", meta)?; builders.try_push(builder)?; cx.index.try_extend(items)?; } - Build::Struct(meta) => { + Build::Trait => { + cx.set_path(meta)?; + let (builder, items) = self::type_::build(&mut cx, "Trait", "trait", meta)?; + builders.try_push(builder)?; + cx.index.try_extend(items)?; + } + Build::Struct => { cx.set_path(meta)?; let (builder, index) = self::type_::build(&mut cx, "Struct", "struct", meta)?; builders.try_push(builder)?; cx.index.try_extend(index)?; } - Build::Enum(meta) => { + Build::Enum => { cx.set_path(meta)?; - let (builder, index) = self::enum_::build(&mut cx, meta)?; + let (builder, index) = self::type_::build(&mut cx, "Enum", "enum", meta)?; builders.try_push(builder)?; cx.index.try_extend(index)?; } - Build::Macro(meta) => { + Build::Macro => { cx.set_path(meta)?; builders.try_push(build_macro(&mut cx, meta)?)?; } - Build::Function(meta) => { + Build::Function => { cx.set_path(meta)?; builders.try_push(build_function(&mut cx, meta)?)?; } - Build::Module(meta) => { + Build::Module => { cx.set_path(meta)?; builders.try_push(module(&mut cx, meta, &mut queue)?)?; let item = meta.item.context("Missing item")?; @@ -270,15 +280,15 @@ struct Shared<'a> { js: Vec, } -#[derive(Default, Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub(crate) enum ItemKind { Type, Struct, Enum, - #[default] Module, Macro, Function, + Trait, } impl fmt::Display for ItemKind { @@ -290,6 +300,7 @@ impl fmt::Display for ItemKind { ItemKind::Module => "module".fmt(f), ItemKind::Macro => "macro".fmt(f), ItemKind::Function => "function".fmt(f), + ItemKind::Trait => "trait".fmt(f), } } } @@ -340,7 +351,6 @@ pub(crate) struct Ctxt<'a, 'm> { type_template: templating::Template, macro_template: templating::Template, function_template: templating::Template, - enum_template: templating::Template, syntax_set: SyntaxSet, tests: Vec, } @@ -354,6 +364,7 @@ impl<'m> Ctxt<'_, 'm> { Kind::Macro => ItemKind::Macro, Kind::Function(..) => ItemKind::Function, Kind::Module => ItemKind::Module, + Kind::Trait => ItemKind::Trait, kind => bail!("Cannot set path for {kind:?}"), }; @@ -495,14 +506,12 @@ impl<'m> Ctxt<'_, 'm> { return Err(error); } - if capture_tests && !tests.is_empty() { - for (content, params) in tests { - self.tests.try_push(Test { - item: self.state.item.try_clone()?, - content, - params, - })?; - } + for (content, params) in tests { + self.tests.try_push(Test { + item: self.state.item.try_clone()?, + content, + params, + })?; } write!(o, "")?; @@ -802,13 +811,14 @@ impl<'m> Ctxt<'_, 'm> { } } -enum Build<'a> { - Type(Meta<'a>), - Struct(Meta<'a>), - Enum(Meta<'a>), - Macro(Meta<'a>), - Function(Meta<'a>), - Module(Meta<'a>), +enum Build { + Type, + Struct, + Enum, + Macro, + Function, + Module, + Trait, } /// Get an asset as a string. @@ -886,7 +896,7 @@ fn build_index<'m>( fn module<'m>( cx: &mut Ctxt<'_, 'm>, meta: Meta<'m>, - queue: &mut VecDeque>, + queue: &mut VecDeque<(Build, Meta<'m>)>, ) -> Result> { #[derive(Serialize)] struct Params<'a> { @@ -902,6 +912,7 @@ fn module<'m>( macros: Vec>, functions: Vec>, modules: Vec>, + traits: Vec>, } #[derive(Serialize)] @@ -967,6 +978,16 @@ fn module<'m>( doc: Option, } + #[derive(Serialize)] + struct Trait<'a> { + #[serde(serialize_with = "serialize_item")] + item: ItemBuf, + #[serde(serialize_with = "serialize_component_ref")] + name: ComponentRef<'a>, + path: RelativePathBuf, + doc: Option, + } + let meta_item = meta.item.context("Missing item")?; let mut types = Vec::new(); @@ -975,6 +996,7 @@ fn module<'m>( let mut macros = Vec::new(); let mut functions = Vec::new(); let mut modules = Vec::new(); + let mut traits = Vec::new(); for (_, name) in cx.context.iter_components(meta_item)? { let item = meta_item.join([name])?; @@ -982,8 +1004,10 @@ fn module<'m>( for m in cx.context.meta(&item)? { match m.kind { Kind::Type { .. } => { - queue.try_push_front(Build::Type(m))?; + queue.try_push_front((Build::Type, m))?; + let path = cx.item_path(&item, ItemKind::Type)?; + types.try_push(Type { item: item.try_clone()?, path, @@ -992,8 +1016,10 @@ fn module<'m>( })?; } Kind::Struct { .. } => { - queue.try_push_front(Build::Struct(m))?; + queue.try_push_front((Build::Struct, m))?; + let path = cx.item_path(&item, ItemKind::Struct)?; + structs.try_push(Struct { item: item.try_clone()?, path, @@ -1002,8 +1028,10 @@ fn module<'m>( })?; } Kind::Enum { .. } => { - queue.try_push_front(Build::Enum(m))?; + queue.try_push_front((Build::Enum, m))?; + let path = cx.item_path(&item, ItemKind::Enum)?; + enums.try_push(Enum { item: item.try_clone()?, path, @@ -1014,7 +1042,7 @@ fn module<'m>( Kind::Macro => { let item = m.item.context("Missing macro item")?; - queue.try_push_front(Build::Macro(m))?; + queue.try_push_front((Build::Macro, m))?; macros.try_push(Macro { path: cx.item_path(item, ItemKind::Macro)?, @@ -1028,7 +1056,7 @@ fn module<'m>( continue; } - queue.try_push_front(Build::Function(m))?; + queue.try_push_front((Build::Function, m))?; functions.try_push(Function { is_async: f.is_async, @@ -1048,7 +1076,8 @@ fn module<'m>( continue; } - queue.try_push_front(Build::Module(m))?; + queue.try_push_front((Build::Module, m))?; + let path = cx.item_path(item, ItemKind::Module)?; let name = item.last().context("missing name of module")?; @@ -1064,6 +1093,18 @@ fn module<'m>( doc: cx.render_line_docs(m, m.docs.get(..1).unwrap_or_default())?, })?; } + Kind::Trait { .. } => { + queue.try_push_front((Build::Trait, m))?; + + let path = cx.item_path(&item, ItemKind::Trait)?; + + traits.try_push(Trait { + item: item.try_clone()?, + path, + name, + doc: cx.render_line_docs(m, m.docs.get(..1).unwrap_or_default())?, + })?; + } _ => { continue; } @@ -1085,6 +1126,7 @@ fn module<'m>( macros, functions, modules, + traits, }) })?) } @@ -1215,6 +1257,7 @@ fn build_item_path( ItemKind::Module => "module.html", ItemKind::Macro => "macro.html", ItemKind::Function => "fn.html", + ItemKind::Trait => "trait.html", }); Ok(()) diff --git a/crates/rune/src/doc/build/enum_.rs b/crates/rune/src/doc/build/enum_.rs deleted file mode 100644 index 0f2508dc8..000000000 --- a/crates/rune/src/doc/build/enum_.rs +++ /dev/null @@ -1,53 +0,0 @@ -use anyhow::{Context, Result}; -use serde::Serialize; - -use crate::alloc::{String, Vec}; -use crate::doc::build::{self, Builder, Ctxt, IndexEntry}; -use crate::doc::context::Meta; -use crate::item::ComponentRef; -use crate::Item; - -#[derive(Serialize)] -struct Params<'a> { - #[serde(flatten)] - shared: build::Shared<'a>, - module: String, - #[serde(serialize_with = "super::serialize_component_ref")] - name: ComponentRef<'a>, - #[serde(serialize_with = "super::serialize_item")] - item: &'a Item, - variants: Vec>, - methods: Vec>, - protocols: Vec>, - doc: Option, -} - -/// Build an enumeration. -#[tracing::instrument(skip_all)] -pub(crate) fn build<'m>( - cx: &mut Ctxt<'_, 'm>, - meta: Meta<'m>, -) -> Result<(Builder<'m>, Vec>)> { - let module = cx.module_path_html(meta, false)?; - - let (protocols, methods, variants, index) = build::type_::build_assoc_fns(cx, meta)?; - let item = meta.item.context("Missing enum item")?; - let name = item.last().context("Missing enum name")?; - - let doc = cx.render_docs(meta, meta.docs, true)?; - - let builder = Builder::new(cx, move |cx| { - cx.enum_template.render(&Params { - shared: cx.shared()?, - module, - name, - item, - variants, - methods, - protocols, - doc, - }) - })?; - - Ok((builder, index)) -} diff --git a/crates/rune/src/doc/build/type_.rs b/crates/rune/src/doc/build/type_.rs index 2cc6d7530..bd9537a6c 100644 --- a/crates/rune/src/doc/build/type_.rs +++ b/crates/rune/src/doc/build/type_.rs @@ -1,14 +1,15 @@ use anyhow::{Context, Result}; +use relative_path::RelativePathBuf; use serde::Serialize; use crate::alloc::borrow::Cow; use crate::alloc::fmt::TryWrite; use crate::alloc::prelude::*; -use crate::alloc::{String, Vec}; -use crate::doc::build::{Builder, Ctxt, IndexEntry, IndexKind}; -use crate::doc::context::{Assoc, AssocFnKind, Meta}; +use crate::doc::context::{Assoc, AssocFnKind, Kind, Meta}; use crate::item::ComponentRef; -use crate::Item; +use crate::{Hash, Item}; + +use super::{Builder, Ctxt, IndexEntry, IndexKind, ItemKind}; #[derive(Serialize)] pub(super) struct Protocol<'a> { @@ -39,6 +40,18 @@ pub(super) struct Variant<'a> { doc: Option, } +#[derive(Default, Serialize)] +pub(super) struct Trait<'a> { + #[serde(serialize_with = "super::serialize_item")] + pub(super) item: &'a Item, + pub(super) hash: Hash, + pub(super) module: String, + pub(super) name: &'a str, + pub(super) url: RelativePathBuf, + pub(super) methods: Vec>, + pub(super) protocols: Vec>, +} + pub(super) fn build_assoc_fns<'m>( cx: &mut Ctxt<'_, 'm>, meta: Meta<'m>, @@ -47,19 +60,97 @@ pub(super) fn build_assoc_fns<'m>( Vec>, Vec>, Vec>, + Vec>, )> { + let meta_item = meta.item.context("Missing meta item")?; + + let (variants, protocols, methods) = associated_for_hash(cx, meta.hash, meta, true)?; + + let mut index = Vec::new(); + + if let Some(name) = cx.state.path.file_name() { + index.try_reserve(methods.len())?; + + for m in &methods { + index.try_push(IndexEntry { + path: cx + .state + .path + .with_file_name(format!("{name}#method.{}", m.name)), + item: Cow::Owned(meta_item.join([m.name])?), + kind: IndexKind::Method, + doc: m.line_doc.try_clone()?, + })?; + } + + for m in &variants { + index.try_push(IndexEntry { + path: cx + .state + .path + .with_file_name(format!("{name}#variant.{}", m.name)), + item: Cow::Owned(meta_item.join([m.name])?), + kind: IndexKind::Variant, + doc: m.line_doc.try_clone()?, + })?; + } + } + + let mut traits = Vec::new(); + + 'outer: for hash in cx.context.traits(meta.hash) { + let item = 'item: { + for meta in cx.context.meta_by_hash(hash)? { + match meta.kind { + Kind::Trait => break 'item meta.item.context("Missing trait item")?, + _ => continue, + } + } + + continue 'outer; + }; + + let (_, protocols, methods) = associated_for_hash(cx, hash, meta, false)?; + + let name = item + .last() + .and_then(|c| c.as_str()) + .context("Missing trait name")?; + + let module = cx.module_path_html(meta, false)?; + let url = cx.item_path(item, ItemKind::Trait)?; + + traits.try_push(Trait { + item, + hash, + module, + name, + url, + methods, + protocols, + })?; + } + + Ok((protocols, methods, variants, index, traits)) +} + +fn associated_for_hash<'m>( + cx: &mut Ctxt<'_, 'm>, + hash: Hash, + meta: Meta<'m>, + capture_tests: bool, +) -> Result<(Vec>, Vec>, Vec>)> { + let mut variants = Vec::new(); let mut protocols = Vec::new(); let mut methods = Vec::new(); - let mut variants = Vec::new(); - let meta_item = meta.item.context("Missing meta item")?; - - for assoc in cx.context.associated(meta.hash) { + for assoc in cx.context.associated(hash) { match assoc { Assoc::Variant(variant) => { let line_doc = cx.render_line_docs(meta, variant.docs.get(..1).unwrap_or_default())?; - let doc = cx.render_docs(meta, variant.docs, true)?; + + let doc = cx.render_docs(meta, variant.docs, capture_tests)?; variants.try_push(Variant { name: variant.name, @@ -85,7 +176,7 @@ pub(super) fn build_assoc_fns<'m>( cx.render_line_docs(meta, assoc.docs.get(..1).unwrap_or_default())?; cx.state.item.push(name)?; - let doc = cx.render_docs(meta, assoc.docs, true)?; + let doc = cx.render_docs(meta, assoc.docs, capture_tests)?; cx.state.item.pop()?; let parameters = if !assoc.parameter_types.is_empty() { @@ -105,7 +196,7 @@ pub(super) fn build_assoc_fns<'m>( None }; - methods.try_push(Method { + let method = Method { is_async: assoc.is_async, deprecated: assoc.deprecated, name, @@ -114,16 +205,22 @@ pub(super) fn build_assoc_fns<'m>( return_type: cx.return_type(assoc.return_type)?, line_doc, doc, - })?; + }; + methods.try_push(method)?; continue; } }; + // NB: Regular associated functions are documented by the trait itself. + if assoc.trait_hash.is_some() { + continue; + } + let doc = if assoc.docs.is_empty() { cx.render_docs(meta, protocol.doc, false)? } else { - cx.render_docs(meta, assoc.docs, true)? + cx.render_docs(meta, assoc.docs, capture_tests)? }; let repr = if let Some(repr) = protocol.repr { @@ -132,49 +229,20 @@ pub(super) fn build_assoc_fns<'m>( None }; - protocols.try_push(Protocol { + let protocol = Protocol { name: protocol.name, field, repr, return_type: cx.return_type(assoc.return_type)?, doc, deprecated: assoc.deprecated, - })?; - } - } - } - - let mut index = Vec::new(); - - if let Some(name) = cx.state.path.file_name() { - index.try_reserve(methods.len())?; - - for m in &methods { - index.try_push(IndexEntry { - path: cx - .state - .path - .with_file_name(format!("{name}#method.{}", m.name)), - item: Cow::Owned(meta_item.join([m.name])?), - kind: IndexKind::Method, - doc: m.line_doc.try_clone()?, - })?; - } + }; - for m in &variants { - index.try_push(IndexEntry { - path: cx - .state - .path - .with_file_name(format!("{name}#variant.{}", m.name)), - item: Cow::Owned(meta_item.join([m.name])?), - kind: IndexKind::Variant, - doc: m.line_doc.try_clone()?, - })?; + protocols.try_push(protocol)?; + } } } - - Ok((protocols, methods, variants, index)) + Ok((variants, protocols, methods)) } #[derive(Serialize)] @@ -190,6 +258,7 @@ struct Params<'a> { item: &'a Item, methods: Vec>, protocols: Vec>, + traits: Vec>, doc: Option, } @@ -203,7 +272,7 @@ pub(crate) fn build<'m>( ) -> Result<(Builder<'m>, Vec>)> { let module = cx.module_path_html(meta, false)?; - let (protocols, methods, _, index) = build_assoc_fns(cx, meta)?; + let (protocols, methods, _, index, traits) = build_assoc_fns(cx, meta)?; let item = meta.item.context("Missing type item")?; let name = item.last().context("Missing module name")?; @@ -219,6 +288,7 @@ pub(crate) fn build<'m>( item, methods, protocols, + traits, doc, }) })?; diff --git a/crates/rune/src/doc/context.rs b/crates/rune/src/doc/context.rs index 66e419959..f17e46ca7 100644 --- a/crates/rune/src/doc/context.rs +++ b/crates/rune/src/doc/context.rs @@ -42,6 +42,7 @@ pub(crate) struct Function<'a> { } /// The kind of an associated function. +#[derive(Debug)] pub(crate) enum AssocFnKind<'a> { /// A protocol function implemented on the type itself. Protocol(Protocol), @@ -54,6 +55,7 @@ pub(crate) enum AssocFnKind<'a> { } /// Information on an associated function. +#[derive(Debug)] pub(crate) struct AssocVariant<'a> { /// Name of variant. pub(crate) name: &'a str, @@ -62,8 +64,10 @@ pub(crate) struct AssocVariant<'a> { } /// Information on an associated function. +#[derive(Debug)] pub(crate) struct AssocFn<'a> { pub(crate) kind: AssocFnKind<'a>, + pub(crate) trait_hash: Option, pub(crate) is_async: bool, pub(crate) arguments: Option<&'a [meta::DocArgument]>, pub(crate) return_type: &'a meta::DocType, @@ -74,6 +78,7 @@ pub(crate) struct AssocFn<'a> { } /// Information on an associated item. +#[derive(Debug)] pub(crate) enum Assoc<'a> { /// A variant, Variant(AssocVariant<'a>), @@ -92,6 +97,7 @@ pub(crate) enum Kind<'a> { Function(Function<'a>), Const(#[allow(unused)] &'a ConstValue), Module, + Trait, } #[derive(Debug, Clone, Copy)] @@ -115,122 +121,6 @@ impl<'a> Context<'a> { /// Iterate over all types associated with the given hash. pub(crate) fn associated(&self, hash: Hash) -> impl Iterator> { - fn visitor_to_associated( - hash: Hash, - visitor: &Visitor, - ) -> Option>> { - let associated = visitor.associated.get(&hash)?; - - Some(associated.iter().flat_map(move |hash| { - let data = visitor.data.get(hash)?; - - let (is_async, kind, arguments, return_type) = match &data.kind { - Some(meta::Kind::Function { - associated: None, - signature: f, - .. - }) => ( - f.is_async, - AssocFnKind::Method(data.item.last()?.as_str()?, Signature::Function), - f.arguments.as_deref(), - &f.return_type, - ), - Some(meta::Kind::Function { - associated: Some(meta::AssociatedKind::Instance(name)), - signature: f, - .. - }) => ( - f.is_async, - AssocFnKind::Method(name.as_ref(), Signature::Instance), - f.arguments.as_deref(), - &f.return_type, - ), - Some(meta::Kind::Variant { .. }) => { - return Some(Assoc::Variant(AssocVariant { - name: data.item.last()?.as_str()?, - docs: &data.docs, - })); - } - _ => return None, - }; - - Some(Assoc::Fn(AssocFn { - kind, - is_async, - arguments, - return_type, - parameter_types: &[], - deprecated: data.deprecated.as_deref(), - docs: &data.docs, - })) - })) - } - - fn context_to_associated(context: &crate::Context, hash: Hash) -> Option> { - let meta = context.lookup_meta_by_hash(hash).next()?; - - match &meta.kind { - meta::Kind::Variant { .. } => { - let name = meta.item.as_deref()?.last()?.as_str()?; - Some(Assoc::Variant(AssocVariant { - name, - docs: meta.docs.lines(), - })) - } - meta::Kind::Function { - associated: Some(associated), - parameter_types, - signature, - .. - } => { - let kind = match *associated { - meta::AssociatedKind::Protocol(protocol) => AssocFnKind::Protocol(protocol), - meta::AssociatedKind::FieldFn(protocol, ref field) => { - AssocFnKind::FieldFn(protocol, field) - } - meta::AssociatedKind::IndexFn(protocol, index) => { - AssocFnKind::IndexFn(protocol, index) - } - meta::AssociatedKind::Instance(ref name) => { - AssocFnKind::Method(name, Signature::Instance) - } - }; - - Some(Assoc::Fn(AssocFn { - kind, - is_async: signature.is_async, - arguments: signature.arguments.as_deref(), - return_type: &signature.return_type, - parameter_types: ¶meter_types[..], - deprecated: meta.deprecated.as_deref(), - docs: meta.docs.lines(), - })) - } - meta::Kind::Function { - associated: None, - signature, - .. - } => { - let name = meta.item.as_deref()?.last()?.as_str()?; - let kind = AssocFnKind::Method(name, Signature::Function); - - Some(Assoc::Fn(AssocFn { - kind, - is_async: signature.is_async, - arguments: signature.arguments.as_deref(), - return_type: &signature.return_type, - parameter_types: &[], - deprecated: meta.deprecated.as_deref(), - docs: meta.docs.lines(), - })) - } - kind => { - tracing::warn!(?kind, "Unsupported associated type"); - None - } - } - } - let visitors = self .visitors .iter() @@ -244,6 +134,11 @@ impl<'a> Context<'a> { visitors.chain(context) } + /// Iterate over all traits associated with the given hash. + pub(crate) fn traits(&self, hash: Hash) -> impl Iterator + 'a { + self.context.into_iter().flat_map(move |c| c.traits(hash)) + } + /// Iterate over known child components of the given name. pub(crate) fn iter_components( &self, @@ -271,7 +166,7 @@ impl<'a> Context<'a> { } /// Get all matching meta items by hash. - pub(crate) fn meta_by_hash(&self, hash: Hash) -> alloc::Result>> { + pub(crate) fn meta_by_hash(&self, hash: Hash) -> alloc::Result>> { let mut out = Vec::new(); for visitor in self.visitors { @@ -340,6 +235,7 @@ impl<'a> Context<'a> { } meta::Kind::Macro => Kind::Macro, meta::Kind::Module { .. } => Kind::Module, + meta::Kind::Trait { .. } => Kind::Trait, _ => Kind::Unsupported, }; @@ -363,6 +259,118 @@ impl<'a> Context<'a> { } } +fn visitor_to_associated(hash: Hash, visitor: &Visitor) -> Option>> { + let associated = visitor.associated.get(&hash)?; + + Some(associated.iter().flat_map(move |hash| { + let data = visitor.data.get(hash)?; + + let (associated, trait_hash, signature) = match &data.kind { + Some(meta::Kind::Function { + associated, + trait_hash, + signature, + .. + }) => (associated, trait_hash, signature), + Some(meta::Kind::Variant { .. }) => { + return Some(Assoc::Variant(AssocVariant { + name: data.item.last()?.as_str()?, + docs: &data.docs, + })); + } + _ => return None, + }; + + let kind = match associated { + Some(meta::AssociatedKind::Instance(name)) => { + AssocFnKind::Method(name.as_ref(), Signature::Instance) + } + None => AssocFnKind::Method(data.item.last()?.as_str()?, Signature::Function), + _ => return None, + }; + + Some(Assoc::Fn(AssocFn { + kind, + trait_hash: *trait_hash, + is_async: signature.is_async, + arguments: signature.arguments.as_deref(), + return_type: &signature.return_type, + parameter_types: &[], + deprecated: data.deprecated.as_deref(), + docs: &data.docs, + })) + })) +} + +fn context_to_associated(context: &crate::Context, hash: Hash) -> Option> { + let meta = context.lookup_meta_by_hash(hash).next()?; + + match &meta.kind { + meta::Kind::Variant { .. } => { + let name = meta.item.as_deref()?.last()?.as_str()?; + Some(Assoc::Variant(AssocVariant { + name, + docs: meta.docs.lines(), + })) + } + meta::Kind::Function { + associated: Some(associated), + trait_hash, + parameter_types, + signature, + .. + } => { + let kind = match *associated { + meta::AssociatedKind::Protocol(protocol) => AssocFnKind::Protocol(protocol), + meta::AssociatedKind::FieldFn(protocol, ref field) => { + AssocFnKind::FieldFn(protocol, field) + } + meta::AssociatedKind::IndexFn(protocol, index) => { + AssocFnKind::IndexFn(protocol, index) + } + meta::AssociatedKind::Instance(ref name) => { + AssocFnKind::Method(name, Signature::Instance) + } + }; + + Some(Assoc::Fn(AssocFn { + kind, + trait_hash: *trait_hash, + is_async: signature.is_async, + arguments: signature.arguments.as_deref(), + return_type: &signature.return_type, + parameter_types: ¶meter_types[..], + deprecated: meta.deprecated.as_deref(), + docs: meta.docs.lines(), + })) + } + meta::Kind::Function { + associated: None, + trait_hash, + signature, + .. + } => { + let name = meta.item.as_deref()?.last()?.as_str()?; + let kind = AssocFnKind::Method(name, Signature::Function); + + Some(Assoc::Fn(AssocFn { + kind, + trait_hash: *trait_hash, + is_async: signature.is_async, + arguments: signature.arguments.as_deref(), + return_type: &signature.return_type, + parameter_types: &[], + deprecated: meta.deprecated.as_deref(), + docs: meta.docs.lines(), + })) + } + kind => { + tracing::warn!(?kind, "Unsupported associated type"); + None + } + } +} + fn visitor_meta_to_meta<'a>(base: &'a Item, data: &'a VisitorData) -> Meta<'a> { let kind = match &data.kind { Some(meta::Kind::Type { .. }) => Kind::Type, diff --git a/crates/rune/src/doc/static/enum.html.hbs b/crates/rune/src/doc/static/enum.html.hbs deleted file mode 100644 index 93fe5fb1c..000000000 --- a/crates/rune/src/doc/static/enum.html.hbs +++ /dev/null @@ -1,46 +0,0 @@ -{{#> layout}} - -{{#if doc}}{{literal doc}}{{/if}} - -{{#if variants}} -

Variants

- -{{#each variants}} -
- - {{#if this.doc}}{{literal this.doc}}{{/if}} -
-{{/each}} -{{/if}} - -{{#if methods}} -

Methods

- -{{#each methods}} -
-
- {{#if this.is_async}}async {{/if}}fn {{this.name}}{{#if this.parameters}}<{{literal this.parameters}}>{{/if}}({{literal this.args}}){{#if this.return_type}} -> {{literal this.return_type}}{{/if}} -
- {{#if this.doc}}{{literal this.doc}}{{/if}} -
-{{/each}} -{{/if}} - -{{#if protocols}} -

Protocols

- -{{#each protocols}} -
-
- protocol {{this.name}} {{field}} - {{#if this.deprecated}}
Deprecated:{{this.deprecated}}
{{/if}} -
- {{#if this.repr}}{{literal this.repr}}{{/if}} - {{#if this.doc}}{{literal this.doc}}{{/if}} -
-{{/each}} -{{/if}} - -{{/layout}} diff --git a/crates/rune/src/doc/static/module.html.hbs b/crates/rune/src/doc/static/module.html.hbs index 5c621ca52..b979ce5be 100644 --- a/crates/rune/src/doc/static/module.html.hbs +++ b/crates/rune/src/doc/static/module.html.hbs @@ -52,6 +52,16 @@ {{/each}} {{/if}} +{{#if traits}} +

Traits

+ +{{#each traits}} +
+ {{this.name}}{{#if this.doc}}{{literal this.doc}}{{/if}} +
+{{/each}} +{{/if}} + {{#if modules}}

Modules

diff --git a/crates/rune/src/doc/static/runedoc.css.hbs b/crates/rune/src/doc/static/runedoc.css.hbs index a2b467dc7..094ab75b8 100644 --- a/crates/rune/src/doc/static/runedoc.css.hbs +++ b/crates/rune/src/doc/static/runedoc.css.hbs @@ -266,6 +266,10 @@ a { color: var(--type-link-color); } +.trait { + color: var(--type-link-color); +} + .fn { color: var(--fn-link-color); } diff --git a/crates/rune/src/doc/static/type.html.hbs b/crates/rune/src/doc/static/type.html.hbs index 820c3741d..ef897b689 100644 --- a/crates/rune/src/doc/static/type.html.hbs +++ b/crates/rune/src/doc/static/type.html.hbs @@ -1,7 +1,25 @@ {{#> layout}} -

{{what}} {{literal module}}::{{name}}

Overview
+ +
+

{{what}} {{literal module}}::{{name}}

+ Overview +
+ {{#if doc}}{{literal doc}}{{/if}} +{{#if variants}} +

Variants

+ +{{#each variants}} +
+ + {{#if this.doc}}{{literal this.doc}}{{/if}} +
+{{/each}} +{{/if}} + {{#if methods}}

Methods

@@ -30,4 +48,38 @@ {{/each}} {{/if}} + +{{#if traits}} +

Traits implemented

+ +{{#each traits}} +
+
+ impl {{literal this.module}}::{{this.name}} +
+
+ + {{#each this.methods}} +
+
+ {{#if this.is_async}}async {{/if}}fn {{this.name}}{{#if this.parameters}}<{{literal this.parameters}}>{{/if}}({{literal this.args}}){{#if this.return_type}} -> {{literal this.return_type}}{{/if}} + {{#if this.deprecated}}
Deprecated:{{this.deprecated}}
{{/if}} +
+ {{#if this.doc}}{{literal this.doc}}{{/if}} +
+ {{/each}} + + {{#each this.protocols}} +
+
+ protocol {{this.name}} {{field}} + {{#if this.deprecated}}
Deprecated:{{this.deprecated}}
{{/if}} +
+ {{#if this.repr}}{{literal this.repr}}{{/if}} + {{#if this.doc}}{{literal this.doc}}{{/if}} +
+ {{/each}} +{{/each}} +{{/if}} + {{/layout}} diff --git a/crates/rune/src/exported_macros.rs b/crates/rune/src/exported_macros.rs index 12f803f69..61589781b 100644 --- a/crates/rune/src/exported_macros.rs +++ b/crates/rune/src/exported_macros.rs @@ -3,7 +3,7 @@ /// This can be used through [`rune::function`] by enabling the `vm_result` /// option and suffixing an expression with `.vm?`. /// -/// [`rune::function`]: crate::function +/// [`rune::function`]: macro@crate::function /// [`VmResult`]: crate::runtime::VmResult #[macro_export] macro_rules! vm_try { @@ -23,7 +23,7 @@ macro_rules! vm_try { /// with [`rune::function`], since a regular return would otherwise be /// transformed. /// -/// [`rune::function`]: crate::function +/// [`rune::function`]: macro@crate::function /// [`VmResult`]: crate::runtime::VmResult /// /// # Examples @@ -60,3 +60,22 @@ macro_rules! vm_write { } }; } + +/// Convenience macro for extracting a documentation string from documentation +/// comments. +/// +/// # Examples +/// +/// ``` +/// let docs: [&'static str; 3] = rune::docstring! { +/// /// Hi, this is some documentation. +/// /// +/// /// I hope you like it! +/// }; +/// ``` +#[macro_export] +macro_rules! docstring { + ($(#[doc = $doc:expr])*) => { + [$($doc),*] + }; +} diff --git a/crates/rune/src/module/function_traits/macros.rs b/crates/rune/src/function/macros.rs similarity index 100% rename from crates/rune/src/module/function_traits/macros.rs rename to crates/rune/src/function/macros.rs diff --git a/crates/rune/src/module/function_traits.rs b/crates/rune/src/function/mod.rs similarity index 88% rename from crates/rune/src/module/function_traits.rs rename to crates/rune/src/function/mod.rs index b70eb38d3..51749a81a 100644 --- a/crates/rune/src/module/function_traits.rs +++ b/crates/rune/src/function/mod.rs @@ -7,8 +7,8 @@ use crate::alloc; use crate::compile::meta; use crate::hash::Hash; use crate::runtime::{ - self, FromValue, InstAddress, MaybeTypeOf, Output, Stack, ToValue, TypeInfo, TypeOf, - UnsafeToMut, UnsafeToRef, Value, VmErrorKind, VmResult, + self, CoreTypeOf, FromValue, InstAddress, MaybeTypeOf, Memory, Output, ToValue, TypeInfo, + TypeOf, UnsafeToMut, UnsafeToRef, Value, VmErrorKind, VmResult, }; // Expand to function variable bindings. @@ -63,6 +63,9 @@ impl FunctionKind for Async { /// Trait used to provide the [function][crate::module::Module::function] /// function. +#[diagnostic::on_unimplemented( + label = "#[derive(Any)] could be missing on the arguments or return value of `{Self}`" +)] pub trait Function: 'static + Send + Sync { /// The return type of the function. #[doc(hidden)] @@ -76,7 +79,7 @@ pub trait Function: 'static + Send + Sync { #[doc(hidden)] fn fn_call( &self, - stack: &mut Stack, + stack: &mut dyn Memory, addr: InstAddress, args: usize, out: Output, @@ -86,6 +89,9 @@ pub trait Function: 'static + Send + Sync { /// Trait used to provide the [`associated_function`] function. /// /// [`associated_function`]: crate::module::Module::associated_function +#[diagnostic::on_unimplemented( + label = "#[derive(Any)] could be missing on the arguments or return value of `{Self}`" +)] pub trait InstanceFunction: 'static + Send + Sync { /// The type of the instance. #[doc(hidden)] @@ -103,7 +109,7 @@ pub trait InstanceFunction: 'static + Send + Sync { #[doc(hidden)] fn fn_call( &self, - stack: &mut Stack, + stack: &mut dyn Memory, addr: InstAddress, args: usize, out: Output, @@ -126,7 +132,7 @@ macro_rules! impl_instance_function_traits { } #[inline] - fn fn_call(&self, stack: &mut Stack, addr: InstAddress, args: usize, out: Output) -> VmResult<()> { + fn fn_call(&self, stack: &mut dyn Memory, addr: InstAddress, args: usize, out: Output) -> VmResult<()> { Function::fn_call(self, stack, addr, args, out) } } @@ -148,6 +154,16 @@ where } } +impl CoreTypeOf for Ref +where + T: ?Sized + CoreTypeOf, +{ + #[inline] + fn type_hash() -> Hash { + T::type_hash() + } +} + impl TypeOf for Ref where T: ?Sized + TypeOf, @@ -157,11 +173,6 @@ where T::type_parameters() } - #[inline] - fn type_hash() -> Hash { - T::type_hash() - } - #[inline] fn type_info() -> TypeInfo { T::type_info() @@ -181,6 +192,16 @@ where } } +impl CoreTypeOf for Mut +where + T: ?Sized + CoreTypeOf, +{ + #[inline] + fn type_hash() -> Hash { + T::type_hash() + } +} + impl TypeOf for Mut where T: ?Sized + TypeOf, @@ -190,11 +211,6 @@ where T::type_parameters() } - #[inline] - fn type_hash() -> Hash { - T::type_hash() - } - #[inline] fn type_info() -> TypeInfo { T::type_info() @@ -244,7 +260,7 @@ macro_rules! impl_function_traits { } #[allow(clippy::drop_non_drop)] - fn fn_call(&self, stack: &mut Stack, addr: InstAddress, args: usize, out: Output) -> VmResult<()> { + fn fn_call(&self, stack: &mut dyn Memory, addr: InstAddress, args: usize, out: Output) -> VmResult<()> { access_stack!($count, 0, stack, addr, args, $($from_fn, $var, $num,)*); // Safety: We hold a reference to the stack, so we can guarantee @@ -272,7 +288,7 @@ macro_rules! impl_function_traits { } #[allow(clippy::drop_non_drop)] - fn fn_call(&self, stack: &mut Stack, addr: InstAddress, args: usize, out: Output) -> VmResult<()> { + fn fn_call(&self, stack: &mut dyn Memory, addr: InstAddress, args: usize, out: Output) -> VmResult<()> { access_stack!($count, 0, stack, addr, args, $($from_fn, $var, $num,)*); let fut = self($($var.0),*); diff --git a/crates/rune/src/function_meta.rs b/crates/rune/src/function_meta.rs index 68a496b4f..ce4074acb 100644 --- a/crates/rune/src/function_meta.rs +++ b/crates/rune/src/function_meta.rs @@ -6,13 +6,13 @@ use crate as rune; use crate::alloc; use crate::alloc::borrow::Cow; use crate::alloc::prelude::*; +use crate::compile::context::{AttributeMacroHandler, MacroHandler}; use crate::compile::{self, meta}; +use crate::function::{Function, FunctionKind, InstanceFunction}; use crate::item::IntoComponent; use crate::macros::{MacroContext, TokenStream}; -use crate::module::{AssociatedKey, Function, FunctionKind, InstanceFunction}; -use crate::runtime::{ - AttributeMacroHandler, FunctionHandler, MacroHandler, MaybeTypeOf, Protocol, TypeInfo, TypeOf, -}; +use crate::module::AssociatedKey; +use crate::runtime::{FunctionHandler, MaybeTypeOf, Protocol, TypeInfo, TypeOf}; use crate::{Hash, ItemBuf}; mod sealed { @@ -537,6 +537,9 @@ pub struct FunctionMetaData { pub trait FunctionArgs { #[doc(hidden)] fn into_box() -> alloc::Result>; + + #[doc(hidden)] + fn len() -> usize; } macro_rules! iter_function_args { @@ -546,10 +549,14 @@ macro_rules! iter_function_args { $($ty: MaybeTypeOf,)* { #[inline] - #[doc(hidden)] fn into_box() -> alloc::Result> { try_vec![$(<$ty as MaybeTypeOf>::maybe_type_of()?),*].try_into_boxed_slice() } + + #[inline] + fn len() -> usize { + $count + } } } } diff --git a/crates/rune/src/hashbrown/mod.rs b/crates/rune/src/hashbrown/mod.rs index 00a7dee10..35f322e1c 100644 --- a/crates/rune/src/hashbrown/mod.rs +++ b/crates/rune/src/hashbrown/mod.rs @@ -1,2 +1,2 @@ -pub(crate) use self::table::{IterRef, Table}; +pub(crate) use self::table::{IterRef, KeysRef, Table, ValuesRef}; mod table; diff --git a/crates/rune/src/hashbrown/table.rs b/crates/rune/src/hashbrown/table.rs index 07407e266..b4a38ce77 100644 --- a/crates/rune/src/hashbrown/table.rs +++ b/crates/rune/src/hashbrown/table.rs @@ -216,6 +216,16 @@ where } } +impl iter::ExactSizeIterator for IterRef +where + V: Clone, +{ + #[inline] + fn len(&self) -> usize { + self.iter.len() + } +} + pub(crate) struct KeysRef { iter: RawIter<(Value, V)>, _guard: RawRef, diff --git a/crates/rune/src/hir/lowering.rs b/crates/rune/src/hir/lowering.rs index b2af2ce3a..d4d8413cf 100644 --- a/crates/rune/src/hir/lowering.rs +++ b/crates/rune/src/hir/lowering.rs @@ -1543,7 +1543,7 @@ pub(crate) fn expr_path<'hir>( let kind = if !parameters.parameters.is_empty() { ErrorKind::MissingItemParameters { item: cx.q.pool.item(named.item).try_to_owned()?, - parameters: parameters.parameters.into_iter().try_collect()?, + parameters: parameters.parameters, } } else { ErrorKind::MissingItem { diff --git a/crates/rune/src/indexing/index.rs b/crates/rune/src/indexing/index.rs index fd416a0bb..39be36613 100644 --- a/crates/rune/src/indexing/index.rs +++ b/crates/rune/src/indexing/index.rs @@ -932,7 +932,7 @@ fn expr_block(idx: &mut Indexer<'_, '_>, ast: &mut ast::ExprBlock) -> compile::R let call = validate_call(ast.const_token.as_ref(), ast.async_token.as_ref(), &layer)?; let Some(call) = call else { - return Err(compile::Error::new(&ast, ErrorKind::ClosureKind)); + return Err(compile::Error::new(ast, ErrorKind::ClosureKind)); }; idx.q.index_meta( diff --git a/crates/rune/src/internal_macros.rs b/crates/rune/src/internal_macros.rs index e67c6a8c6..ca87940b3 100644 --- a/crates/rune/src/internal_macros.rs +++ b/crates/rune/src/internal_macros.rs @@ -11,15 +11,17 @@ macro_rules! resolve_context { /// Build an implementation of `TypeOf` basic of a static type. macro_rules! impl_static_type { (impl <$($p:ident),*> $ty:ty => $static_type:expr) => { - impl<$($p,)*> $crate::runtime::TypeOf for $ty - where - $($p: $crate::runtime::MaybeTypeOf,)* - { + impl<$($p,)*> $crate::runtime::CoreTypeOf for $ty { #[inline] fn type_hash() -> $crate::Hash { $static_type.hash } + } + impl<$($p,)*> $crate::runtime::TypeOf for $ty + where + $($p: $crate::runtime::MaybeTypeOf,)* + { #[inline] fn type_info() -> $crate::runtime::TypeInfo { $crate::runtime::TypeInfo::StaticType($static_type) @@ -33,7 +35,7 @@ macro_rules! impl_static_type { #[inline] fn maybe_type_of() -> $crate::alloc::Result<$crate::compile::meta::DocType> { $crate::compile::meta::DocType::with_generics( - <$ty as $crate::runtime::TypeOf>::type_hash(), + <$ty as $crate::runtime::CoreTypeOf>::type_hash(), [$(<$p as $crate::runtime::MaybeTypeOf>::maybe_type_of()?),*] ) } diff --git a/crates/rune/src/lib.rs b/crates/rune/src/lib.rs index 013932436..8600b118b 100644 --- a/crates/rune/src/lib.rs +++ b/crates/rune/src/lib.rs @@ -216,7 +216,9 @@ pub mod item; pub use self::item::{Item, ItemBuf}; #[doc(hidden)] -pub mod function_meta; +mod function_meta; + +mod function; pub mod module; #[doc(inline)] @@ -627,6 +629,10 @@ pub use rune_macros::macro_; pub use rune_macros::attribute_macro; /// Macro used to annotate a module with metadata. +/// +/// ThIs defines a local function `module_meta` which can be used in conjunction +/// with [`Module::from_meta`] to construct a module with a given item and +/// captured documentation. #[doc(inline)] pub use rune_macros::module; @@ -655,7 +661,7 @@ pub mod __private { pub use crate::item::ItemBuf; pub use crate::module::{InstallWith, Module, ModuleMetaData}; pub use crate::params::Params; - pub use crate::runtime::TypeOf; + pub use crate::runtime::{CoreTypeOf, TypeOf}; pub use rust_alloc::boxed::Box; } diff --git a/crates/rune/src/module/enum_mut.rs b/crates/rune/src/module/enum_mut.rs new file mode 100644 index 000000000..1fc9a8ead --- /dev/null +++ b/crates/rune/src/module/enum_mut.rs @@ -0,0 +1,59 @@ +use core::marker::PhantomData; + +use crate::compile::{ContextError, Docs}; +use crate::runtime::TypeOf; + +use super::{Enum, VariantMut}; + +/// Access enum metadata mutably. +pub struct EnumMut<'a, T> +where + T: ?Sized + TypeOf, +{ + pub(super) docs: &'a mut Docs, + pub(super) enum_: &'a mut Enum, + pub(super) _marker: PhantomData, +} + +impl EnumMut<'_, T> +where + T: ?Sized + TypeOf, +{ + /// Set documentation for an inserted type. + /// + /// This completely replaces any existing documentation. + pub fn docs(self, docs: I) -> Result + where + I: IntoIterator, + I::Item: AsRef, + { + self.docs.set_docs(docs)?; + Ok(self) + } + + /// Set static documentation. + /// + /// This completely replaces any existing documentation. + pub fn static_docs(self, docs: &'static [&'static str]) -> Result { + self.docs.set_docs(docs)?; + Ok(self) + } + + /// Get the given variant mutably. + pub fn variant_mut(&mut self, index: usize) -> Result, ContextError> { + let Some(variant) = self.enum_.variants.get_mut(index) else { + return Err(ContextError::MissingVariant { + index, + type_info: T::type_info(), + }); + }; + + Ok(VariantMut { + index, + docs: &mut variant.docs, + fields: &mut variant.fields, + constructor: &mut variant.constructor, + _marker: PhantomData, + }) + } +} diff --git a/crates/rune/src/module/install_with.rs b/crates/rune/src/module/install_with.rs new file mode 100644 index 000000000..f65d4053b --- /dev/null +++ b/crates/rune/src/module/install_with.rs @@ -0,0 +1,12 @@ +use crate::ContextError; + +use super::Module; + +/// Trait to handle the installation of auxilliary functions for a type +/// installed into a module. +pub trait InstallWith { + /// Hook to install more things into the module. + fn install_with(_: &mut Module) -> Result<(), ContextError> { + Ok(()) + } +} diff --git a/crates/rune/src/module/internal_enum.rs b/crates/rune/src/module/internal_enum.rs new file mode 100644 index 000000000..844a38a14 --- /dev/null +++ b/crates/rune/src/module/internal_enum.rs @@ -0,0 +1,61 @@ +use ::rust_alloc::sync::Arc; + +use crate::alloc::{self, Vec}; +use crate::compile::Docs; +use crate::function::{Function, Plain}; +use crate::runtime::{FunctionHandler, StaticType, TypeCheck}; + +use super::{Fields, ItemMut, Variant}; + +/// Specialized information on `GeneratorState` types. +pub(crate) struct InternalEnum { + /// The name of the internal enum. + pub(crate) name: &'static str, + /// The static type of the enum. + pub(crate) static_type: &'static StaticType, + /// Internal variants. + pub(crate) variants: Vec, +} + +impl InternalEnum { + /// Construct a new handler for an internal enum. + pub(super) fn new(name: &'static str, static_type: &'static StaticType) -> Self { + InternalEnum { + name, + static_type, + variants: Vec::new(), + } + } + + /// Register a new variant. + pub(super) fn variant( + &mut self, + name: &'static str, + type_check: TypeCheck, + constructor: C, + ) -> alloc::Result> + where + C: Function, + { + let constructor: Arc = Arc::new(move |stack, addr, args, output| { + constructor.fn_call(stack, addr, args, output) + }); + + self.variants.try_push(Variant { + name, + type_check: Some(type_check), + fields: Some(Fields::Unnamed(C::args())), + constructor: Some(constructor), + deprecated: None, + docs: Docs::EMPTY, + })?; + + let v = self.variants.last_mut().unwrap(); + + Ok(ItemMut { + docs: &mut v.docs, + #[cfg(feature = "doc")] + deprecated: &mut v.deprecated, + }) + } +} diff --git a/crates/rune/src/module/internal_enum_mut.rs b/crates/rune/src/module/internal_enum_mut.rs new file mode 100644 index 000000000..17ff6f9ea --- /dev/null +++ b/crates/rune/src/module/internal_enum_mut.rs @@ -0,0 +1,75 @@ +use core::marker::PhantomData; + +use crate::compile::ContextError; +use crate::runtime::TypeOf; + +use super::{InternalEnum, ModuleItemCommon, VariantMut}; + +/// Access internal enum metadata mutably. +pub struct InternalEnumMut<'a, T> +where + T: ?Sized + TypeOf, +{ + pub(super) enum_: &'a mut InternalEnum, + pub(super) common: &'a mut ModuleItemCommon, + pub(super) _marker: PhantomData, +} + +impl InternalEnumMut<'_, T> +where + T: ?Sized + TypeOf, +{ + /// Set documentation for an inserted internal enum. + /// + /// This completely replaces any existing documentation. + pub fn docs(self, docs: I) -> Result + where + I: IntoIterator, + I::Item: AsRef, + { + self.common.docs.set_docs(docs)?; + Ok(self) + } + + /// Set static documentation for an inserted internal enum. + /// + /// This completely replaces any existing documentation. + pub fn static_docs(self, docs: &'static [&'static str]) -> Result { + self.common.docs.set_docs(docs)?; + Ok(self) + } + + /// Mark the given type as deprecated. + pub fn deprecated( + self, + #[cfg_attr(not(feature = "doc"), allow(unused))] deprecated: S, + ) -> Result + where + S: AsRef, + { + #[cfg(feature = "doc")] + { + self.common.deprecated = Some(deprecated.as_ref().try_into()?); + } + + Ok(self) + } + + /// Get the given variant mutably. + pub fn variant_mut(&mut self, index: usize) -> Result, ContextError> { + let Some(variant) = self.enum_.variants.get_mut(index) else { + return Err(ContextError::MissingVariant { + index, + type_info: T::type_info(), + }); + }; + + Ok(VariantMut { + index, + docs: &mut variant.docs, + fields: &mut variant.fields, + constructor: &mut variant.constructor, + _marker: PhantomData, + }) + } +} diff --git a/crates/rune/src/module/item_fn_mut.rs b/crates/rune/src/module/item_fn_mut.rs new file mode 100644 index 000000000..3e4085615 --- /dev/null +++ b/crates/rune/src/module/item_fn_mut.rs @@ -0,0 +1,119 @@ +use core::fmt; + +#[cfg(feature = "doc")] +use crate::alloc::Box; +#[cfg(feature = "doc")] +use crate::compile::meta; +use crate::compile::{ContextError, Docs}; +use crate::function_meta::FunctionArgs; +use crate::runtime::MaybeTypeOf; + +/// Handle to a an item inserted into a module which allows for mutation of item +/// metadata. +/// +/// This is returned by methods which insert meta items, such as: +/// * [`Module::raw_fn`]. +/// * [`Module::function`]. +/// * [`Module::associated_function`]. +/// +/// While this is also returned by `*_meta` inserting functions, it is instead +/// recommended that you make use of the appropriate macro to capture doc +/// comments instead: +/// * [`Module::macro_meta`]. +/// * [`Module::function_meta`]. +/// +/// [`Module::raw_fn`]: super::Module::raw_fn +/// [`Module::function`]: super::Module::function +/// [`Module::associated_function`]: super::Module::associated_function +/// [`Module::macro_meta`]: super::Module::macro_meta +/// [`Module::function_meta`]: super::Module::function_meta +pub struct ItemFnMut<'a> { + pub(super) docs: &'a mut Docs, + #[cfg(feature = "doc")] + pub(super) deprecated: &'a mut Option>, + #[cfg(feature = "doc")] + pub(super) is_async: &'a mut bool, + #[cfg(feature = "doc")] + pub(super) args: &'a mut Option, + #[cfg(feature = "doc")] + pub(super) argument_types: &'a mut Box<[meta::DocType]>, + #[cfg(feature = "doc")] + pub(super) return_type: &'a mut meta::DocType, +} + +impl ItemFnMut<'_> { + /// Set documentation for an inserted item. + /// + /// This completely replaces any existing documentation. + pub fn docs(self, docs: impl IntoIterator>) -> Result { + self.docs.set_docs(docs)?; + Ok(self) + } + + /// Mark the given item as an async function. + pub fn is_async(self, #[cfg_attr(not(feature = "doc"), allow(unused))] is_async: bool) -> Self { + #[cfg(feature = "doc")] + { + *self.is_async = is_async; + } + + self + } + + /// Mark the given item as deprecated. + pub fn deprecated( + self, + #[cfg_attr(not(feature = "doc"), allow(unused))] deprecated: impl AsRef, + ) -> Result { + #[cfg(feature = "doc")] + { + *self.deprecated = Some(deprecated.as_ref().try_into()?); + } + + Ok(self) + } + + /// Indicate the number of arguments this function accepts. + pub fn args(self, #[cfg_attr(not(feature = "doc"), allow(unused))] args: usize) -> Self { + #[cfg(feature = "doc")] + { + *self.args = Some(args); + } + + self + } + + /// Set the kind of return type. + pub fn return_type(self) -> Result + where + T: MaybeTypeOf, + { + #[cfg(feature = "doc")] + { + *self.return_type = T::maybe_type_of()?; + } + + Ok(self) + } + + /// Set argument types. + pub fn argument_types(self) -> Result + where + A: FunctionArgs, + { + #[cfg(feature = "doc")] + { + *self.argument_types = A::into_box()?; + *self.args = Some(A::len()); + } + + Ok(self) + } +} + +impl fmt::Debug for ItemFnMut<'_> { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ItemFnMut").finish_non_exhaustive() + } +} diff --git a/crates/rune/src/module/item_mut.rs b/crates/rune/src/module/item_mut.rs new file mode 100644 index 000000000..6166bbaea --- /dev/null +++ b/crates/rune/src/module/item_mut.rs @@ -0,0 +1,54 @@ +use core::fmt; + +#[cfg(feature = "doc")] +use crate::alloc::Box; +use crate::compile::{ContextError, Docs}; + +/// Handle to a an item inserted into a module which allows for mutation of item +/// metadata. +pub struct ItemMut<'a> { + pub(super) docs: &'a mut Docs, + #[cfg(feature = "doc")] + pub(super) deprecated: &'a mut Option>, +} + +impl ItemMut<'_> { + /// Set documentation for an inserted item. + /// + /// This completely replaces any existing documentation. + pub fn docs(self, docs: impl IntoIterator>) -> Result { + self.docs.set_docs(docs)?; + Ok(self) + } + + /// Set static documentation. + /// + /// This completely replaces any existing documentation. + pub fn static_docs(self, docs: &'static [&'static str]) -> Result { + self.docs.set_docs(docs)?; + Ok(self) + } + + /// Mark the given item as deprecated. + pub fn deprecated( + self, + #[cfg_attr(not(feature = "doc"), allow(unused))] deprecated: S, + ) -> Result + where + S: AsRef, + { + #[cfg(feature = "doc")] + { + *self.deprecated = Some(deprecated.as_ref().try_into()?); + } + + Ok(self) + } +} + +impl fmt::Debug for ItemMut<'_> { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ItemMut").finish_non_exhaustive() + } +} diff --git a/crates/rune/src/module/mod.rs b/crates/rune/src/module/mod.rs index 4cfe0b803..983e98aaa 100644 --- a/crates/rune/src/module/mod.rs +++ b/crates/rune/src/module/mod.rs @@ -3,760 +3,64 @@ //! A native module is one that provides rune with functions and types through //! native Rust-based code. -mod function_traits; pub(crate) mod module; -use core::fmt; -use core::marker::PhantomData; +pub(crate) mod install_with; +#[doc(inline)] +pub use self::install_with::InstallWith; -use ::rust_alloc::sync::Arc; +mod internal_enum; +use self::internal_enum::InternalEnum; -use crate as rune; -use crate::alloc::prelude::*; -use crate::alloc::{self, Box, Vec}; -use crate::compile::{meta, ContextError, Docs}; -use crate::function_meta::AssociatedName; -use crate::runtime::{ - AttributeMacroHandler, ConstValue, FunctionHandler, MacroHandler, MaybeTypeOf, StaticType, - TypeCheck, TypeInfo, TypeOf, +mod module_meta; +pub(crate) use self::module_meta::{ + AssociatedKey, DocFunction, Fields, ModuleAssociated, ModuleAssociatedKind, ModuleFunction, + ModuleItem, ModuleItemCommon, ModuleItemKind, ModuleReexport, ModuleTrait, ModuleTraitImpl, + ModuleType, TraitFunction, TypeSpecification, }; -use crate::{Hash, Item, ItemBuf}; +use self::module_meta::{Enum, ModuleAttributeMacro, ModuleMacro, Variant}; +#[doc(inline)] +pub use self::module_meta::{ModuleMeta, ModuleMetaData}; -pub use self::function_traits::{Async, Function, FunctionKind, InstanceFunction, Plain}; -#[doc(hidden)] -pub use self::module::{ - Module, ModuleConstantBuilder, ModuleFunctionBuilder, ModuleMeta, ModuleMetaData, - ModuleRawFunctionBuilder, -}; - -/// Trait to handle the installation of auxilliary functions for a type -/// installed into a module. -pub trait InstallWith { - /// Hook to install more things into the module. - fn install_with(_: &mut Module) -> Result<(), ContextError> { - Ok(()) - } -} - -/// Specialized information on `GeneratorState` types. -pub(crate) struct InternalEnum { - /// The name of the internal enum. - pub(crate) name: &'static str, - /// The static type of the enum. - pub(crate) static_type: &'static StaticType, - /// Internal variants. - pub(crate) variants: Vec, -} - -impl InternalEnum { - /// Construct a new handler for an internal enum. - fn new(name: &'static str, static_type: &'static StaticType) -> Self { - InternalEnum { - name, - static_type, - variants: Vec::new(), - } - } - - /// Register a new variant. - fn variant( - &mut self, - name: &'static str, - type_check: TypeCheck, - constructor: C, - ) -> alloc::Result> - where - C: Function, - { - let constructor: Arc = Arc::new(move |stack, addr, args, output| { - constructor.fn_call(stack, addr, args, output) - }); - - self.variants.try_push(Variant { - name, - type_check: Some(type_check), - fields: Some(Fields::Unnamed(C::args())), - constructor: Some(constructor), - deprecated: None, - docs: Docs::EMPTY, - })?; - - let v = self.variants.last_mut().unwrap(); - - Ok(ItemMut { - docs: &mut v.docs, - #[cfg(feature = "doc")] - deprecated: &mut v.deprecated, - }) - } -} - -/// Data for an opaque type. If `spec` is set, indicates things which are known -/// about that type. -pub(crate) struct ModuleType { - /// The name of the installed type which will be the final component in the - /// item it will constitute. - pub(crate) item: ItemBuf, - /// Common item metadata. - pub(crate) common: ModuleItemCommon, - /// Type hash. - pub(crate) hash: Hash, - /// Type parameters for this item. - pub(crate) type_parameters: Hash, - /// Type information for the installed type. - pub(crate) type_info: TypeInfo, - /// The specification for the type. - pub(crate) spec: Option, - /// Handler to use if this type can be constructed through a regular function call. - pub(crate) constructor: Option>, -} - -/// The kind of the variant. -#[derive(Debug)] -pub(crate) enum Fields { - /// Sequence of named fields. - Named(&'static [&'static str]), - /// Sequence of unnamed fields. - Unnamed(usize), - /// Empty. - Empty, -} - -/// Metadata about a variant. -pub struct Variant { - /// The name of the variant. - pub(crate) name: &'static str, - /// Type check for the variant. - pub(crate) type_check: Option, - /// Variant metadata. - pub(crate) fields: Option, - /// Handler to use if this variant can be constructed through a regular function call. - pub(crate) constructor: Option>, - /// Variant deprecation. - pub(crate) deprecated: Option>, - /// Variant documentation. - pub(crate) docs: Docs, -} - -impl Variant { - fn new(name: &'static str) -> Self { - Self { - name, - type_check: None, - fields: None, - constructor: None, - deprecated: None, - docs: Docs::EMPTY, - } - } -} - -impl fmt::Debug for Variant { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut f = f.debug_struct("Variant"); - f.field("fields", &self.fields); - f.field("constructor", &self.constructor.is_some()); - #[cfg(feature = "doc")] - f.field("deprecated", &self.deprecated); - f.field("docs", &self.docs); - f.finish() - } -} - -/// The type specification for a native enum. -pub(crate) struct Enum { - /// The variants. - pub(crate) variants: Vec, -} - -/// A type specification. -pub(crate) enum TypeSpecification { - Struct(Fields), - Enum(Enum), -} - -/// A key that identifies an associated function. -#[derive(Debug, TryClone, PartialEq, Eq, Hash)] -#[non_exhaustive] -pub(crate) struct AssociatedKey { - /// The type the associated function belongs to. - pub(crate) type_hash: Hash, - /// The kind of the associated function. - pub(crate) kind: meta::AssociatedKind, - /// The type parameters of the associated function. - pub(crate) parameters: Hash, -} - -pub(crate) enum ModuleItemKind { - Constant(ConstValue), - Function(ModuleFunction), - Macro(ModuleMacro), - AttributeMacro(ModuleAttributeMacro), - InternalEnum(InternalEnum), -} - -pub(crate) struct ModuleItem { - pub(crate) item: ItemBuf, - pub(crate) common: ModuleItemCommon, - pub(crate) kind: ModuleItemKind, -} - -#[derive(TryClone)] -pub(crate) struct ModuleFunction { - pub(crate) handler: Arc, - #[cfg(feature = "doc")] - pub(crate) is_async: bool, - #[cfg(feature = "doc")] - pub(crate) args: Option, - #[cfg(feature = "doc")] - pub(crate) argument_types: Box<[meta::DocType]>, - #[cfg(feature = "doc")] - pub(crate) return_type: meta::DocType, -} - -#[derive(TryClone)] -pub(crate) enum ModuleAssociatedKind { - Constant(ConstValue), - Function(ModuleFunction), -} - -#[derive(Default, TryClone)] -pub(crate) struct ModuleItemCommon { - /// Documentation for the item. - pub(crate) docs: Docs, - /// Deprecation marker for the item. - pub(crate) deprecated: Option>, -} - -#[derive(TryClone)] -pub(crate) struct ModuleAssociated { - pub(crate) container: Hash, - pub(crate) container_type_info: TypeInfo, - pub(crate) name: AssociatedName, - pub(crate) common: ModuleItemCommon, - pub(crate) kind: ModuleAssociatedKind, -} - -/// Handle to a macro inserted into a module. -pub(crate) struct ModuleMacro { - pub(crate) handler: Arc, -} - -/// Handle to an attribute macro inserted into a module. -pub(crate) struct ModuleAttributeMacro { - pub(crate) handler: Arc, -} - -/// Handle to a an item inserted into a module which allows for mutation of item -/// metadata. -pub struct ItemMut<'a> { - docs: &'a mut Docs, - #[cfg(feature = "doc")] - deprecated: &'a mut Option>, -} - -impl ItemMut<'_> { - /// Set documentation for an inserted item. - /// - /// This completely replaces any existing documentation. - pub fn docs(self, docs: I) -> Result - where - I: IntoIterator, - I::Item: AsRef, - { - self.docs.set_docs(docs)?; - Ok(self) - } - - /// Set static documentation. - /// - /// This completely replaces any existing documentation. - pub fn static_docs(self, docs: &'static [&'static str]) -> Result { - self.docs.set_docs(docs)?; - Ok(self) - } - - /// Mark the given item as deprecated. - pub fn deprecated( - self, - #[cfg_attr(not(feature = "doc"), allow(unused))] deprecated: S, - ) -> Result - where - S: AsRef, - { - #[cfg(feature = "doc")] - { - *self.deprecated = Some(deprecated.as_ref().try_into()?); - } - - Ok(self) - } -} - -impl fmt::Debug for ItemMut<'_> { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ItemMut").finish_non_exhaustive() - } -} - -/// Handle to a an item inserted into a module which allows for mutation of item -/// metadata. -/// -/// This is returned by methods which insert meta items, such as: -/// * [`Module::raw_fn`]. -/// * [`Module::function`]. -/// * [`Module::associated_function`]. -/// -/// While this is also returned by `*_meta` inserting functions, it is instead -/// recommended that you make use of the appropriate macro to capture doc -/// comments instead: -/// * [`Module::macro_meta`]. -/// * [`Module::function_meta`]. -pub struct ItemFnMut<'a> { - docs: &'a mut Docs, - #[cfg(feature = "doc")] - deprecated: &'a mut Option>, - #[cfg(feature = "doc")] - is_async: &'a mut bool, - #[cfg(feature = "doc")] - args: &'a mut Option, - #[cfg(feature = "doc")] - argument_types: &'a mut Box<[meta::DocType]>, - #[cfg(feature = "doc")] - return_type: &'a mut meta::DocType, -} - -impl ItemFnMut<'_> { - /// Set documentation for an inserted item. - /// - /// This completely replaces any existing documentation. - pub fn docs(self, docs: I) -> Result - where - I: IntoIterator, - I::Item: AsRef, - { - self.docs.set_docs(docs)?; - Ok(self) - } - - /// Mark the given item as an async function. - pub fn is_async(self, #[cfg_attr(not(feature = "doc"), allow(unused))] is_async: bool) -> Self { - #[cfg(feature = "doc")] - { - *self.is_async = is_async; - } - - self - } - - /// Mark the given item as deprecated. - pub fn deprecated( - self, - #[cfg_attr(not(feature = "doc"), allow(unused))] deprecated: S, - ) -> Result - where - S: AsRef, - { - #[cfg(feature = "doc")] - { - *self.deprecated = Some(deprecated.as_ref().try_into()?); - } - - Ok(self) - } - - /// Indicate the number of arguments this function accepts. - pub fn args(self, #[cfg_attr(not(feature = "doc"), allow(unused))] args: usize) -> Self { - #[cfg(feature = "doc")] - { - *self.args = Some(args); - } - - self - } - - /// Set the kind of return type. - pub fn return_type(self) -> Result - where - T: MaybeTypeOf, - { - #[cfg(feature = "doc")] - { - *self.return_type = T::maybe_type_of()?; - } - - Ok(self) - } - - /// Set argument types. - pub fn argument_types( - self, - #[cfg_attr(not(feature = "doc"), allow(unused))] arguments: [meta::DocType; N], - ) -> Result { - #[cfg(feature = "doc")] - { - *self.argument_types = Box::try_from(arguments)?; - } - - Ok(self) - } -} - -impl fmt::Debug for ItemFnMut<'_> { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ItemMut").finish_non_exhaustive() - } -} - -/// Handle to a a variant inserted into a module which allows for mutation of -/// its metadata. -pub struct VariantMut<'a, T> -where - T: ?Sized + TypeOf, -{ - pub(crate) index: usize, - pub(crate) docs: &'a mut Docs, - pub(crate) fields: &'a mut Option, - pub(crate) constructor: &'a mut Option>, - pub(crate) _marker: PhantomData, -} - -impl VariantMut<'_, T> -where - T: ?Sized + TypeOf, -{ - /// Set documentation for an inserted type. - /// - /// This completely replaces any existing documentation. - pub fn docs(self, docs: I) -> Result - where - I: IntoIterator, - I::Item: AsRef, - { - self.docs.set_docs(docs)?; - Ok(self) - } - - /// Set static documentation. - /// - /// This completely replaces any existing documentation. - pub fn static_docs(self, docs: &'static [&'static str]) -> Result { - self.docs.set_docs(docs)?; - Ok(self) - } - - /// Mark the given variant with named fields. - pub fn make_named(self, fields: &'static [&'static str]) -> Result { - self.make(Fields::Named(fields)) - } - - /// Mark the given variant with unnamed fields. - pub fn make_unnamed(self, fields: usize) -> Result { - self.make(Fields::Unnamed(fields)) - } - - /// Mark the given variant as empty. - pub fn make_empty(self) -> Result { - self.make(Fields::Empty) - } - - /// Register a constructor method for the current variant. - pub fn constructor(self, constructor: F) -> Result - where - F: Function, - { - if self.constructor.is_some() { - return Err(ContextError::VariantConstructorConflict { - type_info: T::type_info(), - index: self.index, - }); - } - - *self.constructor = Some(Arc::new(move |stack, addr, args, output| { - constructor.fn_call(stack, addr, args, output) - })); - - Ok(self) - } - - fn make(self, fields: Fields) -> Result { - let old = self.fields.replace(fields); - - if old.is_some() { - return Err(ContextError::ConflictingVariantMeta { - index: self.index, - type_info: T::type_info(), - }); - } - - Ok(self) - } -} - -/// Access enum metadata mutably. -pub struct EnumMut<'a, T> -where - T: ?Sized + TypeOf, -{ - docs: &'a mut Docs, - enum_: &'a mut Enum, - _marker: PhantomData, -} - -impl EnumMut<'_, T> -where - T: ?Sized + TypeOf, -{ - /// Set documentation for an inserted type. - /// - /// This completely replaces any existing documentation. - pub fn docs(self, docs: I) -> Result - where - I: IntoIterator, - I::Item: AsRef, - { - self.docs.set_docs(docs)?; - Ok(self) - } - - /// Set static documentation. - /// - /// This completely replaces any existing documentation. - pub fn static_docs(self, docs: &'static [&'static str]) -> Result { - self.docs.set_docs(docs)?; - Ok(self) - } - - /// Get the given variant mutably. - pub fn variant_mut(&mut self, index: usize) -> Result, ContextError> { - let Some(variant) = self.enum_.variants.get_mut(index) else { - return Err(ContextError::MissingVariant { - index, - type_info: T::type_info(), - }); - }; - - Ok(VariantMut { - index, - docs: &mut variant.docs, - fields: &mut variant.fields, - constructor: &mut variant.constructor, - _marker: PhantomData, - }) - } -} - -/// Access internal enum metadata mutably. -pub struct InternalEnumMut<'a, T> -where - T: ?Sized + TypeOf, -{ - enum_: &'a mut InternalEnum, - common: &'a mut ModuleItemCommon, - _marker: PhantomData, -} - -impl InternalEnumMut<'_, T> -where - T: ?Sized + TypeOf, -{ - /// Set documentation for an inserted internal enum. - /// - /// This completely replaces any existing documentation. - pub fn docs(self, docs: I) -> Result - where - I: IntoIterator, - I::Item: AsRef, - { - self.common.docs.set_docs(docs)?; - Ok(self) - } - - /// Set static documentation for an inserted internal enum. - /// - /// This completely replaces any existing documentation. - pub fn static_docs(self, docs: &'static [&'static str]) -> Result { - self.common.docs.set_docs(docs)?; - Ok(self) - } - - /// Mark the given type as deprecated. - pub fn deprecated( - self, - #[cfg_attr(not(feature = "doc"), allow(unused))] deprecated: S, - ) -> Result - where - S: AsRef, - { - #[cfg(feature = "doc")] - { - self.common.deprecated = Some(deprecated.as_ref().try_into()?); - } - - Ok(self) - } - - /// Get the given variant mutably. - pub fn variant_mut(&mut self, index: usize) -> Result, ContextError> { - let Some(variant) = self.enum_.variants.get_mut(index) else { - return Err(ContextError::MissingVariant { - index, - type_info: T::type_info(), - }); - }; - - Ok(VariantMut { - index, - docs: &mut variant.docs, - fields: &mut variant.fields, - constructor: &mut variant.constructor, - _marker: PhantomData, - }) - } -} - -/// Handle to a a type inserted into a module which allows for mutation of its -/// metadata. -/// -/// This is returned by the following methods: -/// * [`Module::ty`] - after a type has been inserted. -/// * [`Module::type_meta`] - to modify type metadata for an already inserted -/// type. -pub struct TypeMut<'a, T> -where - T: ?Sized + TypeOf, -{ - docs: &'a mut Docs, - #[cfg(feature = "doc")] - deprecated: &'a mut Option>, - spec: &'a mut Option, - constructor: &'a mut Option>, - item: &'a Item, - _marker: PhantomData, -} - -impl<'a, T> TypeMut<'a, T> -where - T: ?Sized + TypeOf, -{ - /// Set documentation for an inserted type. - /// - /// This completely replaces any existing documentation. - pub fn docs(self, docs: I) -> Result - where - I: IntoIterator, - I::Item: AsRef, - { - self.docs.set_docs(docs)?; - Ok(self) - } - - /// Set static documentation. - /// - /// This completely replaces any existing documentation. - pub fn static_docs(self, docs: &'static [&'static str]) -> Result { - self.docs.set_docs(docs)?; - Ok(self) - } - - /// Mark the given type as deprecated. - pub fn deprecated( - self, - #[cfg_attr(not(feature = "doc"), allow(unused))] deprecated: S, - ) -> Result - where - S: AsRef, - { - #[cfg(feature = "doc")] - { - *self.deprecated = Some(deprecated.as_ref().try_into()?); - } - - Ok(self) - } - - /// Mark the current type as a struct with named fields. - pub fn make_named_struct(self, fields: &'static [&'static str]) -> Result { - self.make_struct(Fields::Named(fields)) - } - - /// Mark the current type as a struct with unnamed fields. - pub fn make_unnamed_struct(self, fields: usize) -> Result { - self.make_struct(Fields::Unnamed(fields)) - } - - /// Mark the current type as an empty struct. - pub fn make_empty_struct(self) -> Result { - self.make_struct(Fields::Empty) - } - - /// Mark the current type as an enum. - pub fn make_enum( - self, - variants: &'static [&'static str], - ) -> Result, ContextError> { - let old = self.spec.replace(TypeSpecification::Enum(Enum { - variants: variants.iter().copied().map(Variant::new).try_collect()?, - })); +mod item_mut; +#[doc(inline)] +pub use self::item_mut::ItemMut; - if old.is_some() { - return Err(ContextError::ConflictingTypeMeta { - item: self.item.try_to_owned()?, - type_info: T::type_info(), - }); - } +mod trait_mut; +#[doc(inline)] +pub use self::trait_mut::TraitMut; - let Some(TypeSpecification::Enum(enum_)) = self.spec.as_mut() else { - panic!("Not an enum"); - }; +mod item_fn_mut; +#[doc(inline)] +pub use self::item_fn_mut::ItemFnMut; - Ok(EnumMut { - docs: self.docs, - enum_, - _marker: PhantomData, - }) - } +mod variant_mut; +#[doc(inline)] +pub use self::variant_mut::VariantMut; - /// Register a constructor method for the current type. - pub fn constructor(self, constructor: F) -> Result - where - F: Function, - { - if self.constructor.is_some() { - return Err(ContextError::ConstructorConflict { - type_info: T::type_info(), - }); - } +mod enum_mut; +#[doc(inline)] +pub use self::enum_mut::EnumMut; - *self.constructor = Some(Arc::new(move |stack, addr, args, output| { - constructor.fn_call(stack, addr, args, output) - })); +mod internal_enum_mut; +#[doc(inline)] +pub use self::internal_enum_mut::InternalEnumMut; - Ok(self) - } +mod type_mut; +#[doc(inline)] +pub use self::type_mut::TypeMut; - fn make_struct(self, fields: Fields) -> Result { - let old = self.spec.replace(TypeSpecification::Struct(fields)); +mod module_function_builder; +#[doc(inline)] +pub use self::module_function_builder::ModuleFunctionBuilder; - if old.is_some() { - return Err(ContextError::ConflictingTypeMeta { - item: self.item.try_to_owned()?, - type_info: T::type_info(), - }); - } +mod module_raw_function_builder; +#[doc(inline)] +pub use self::module_raw_function_builder::ModuleRawFunctionBuilder; - Ok(self) - } -} +mod module_constant_builder; +#[doc(inline)] +pub use self::module_constant_builder::ModuleConstantBuilder; -impl fmt::Debug for TypeMut<'_, T> -where - T: TypeOf, -{ - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TypeMut").finish_non_exhaustive() - } -} +#[doc(inline)] +pub use self::module::Module; diff --git a/crates/rune/src/module/module.rs b/crates/rune/src/module/module.rs index 7f0ddf28a..21e91a636 100644 --- a/crates/rune/src/module/module.rs +++ b/crates/rune/src/module/module.rs @@ -4,8 +4,10 @@ use ::rust_alloc::sync::Arc; use crate as rune; use crate::alloc::prelude::*; -use crate::alloc::{self, Box, HashMap, HashSet, String, Vec}; +use crate::alloc::{Box, HashMap, HashSet, String, Vec}; +use crate::compile::context::{AttributeMacroHandler, MacroHandler}; use crate::compile::{self, meta, ContextError, Docs, Named}; +use crate::function::{Async, Function, FunctionKind, InstanceFunction, Plain}; use crate::function_meta::{ Associated, AssociatedFunctionData, AssociatedName, FunctionArgs, FunctionBuilder, FunctionData, FunctionMeta, FunctionMetaKind, MacroMeta, MacroMetaKind, ToFieldFunction, @@ -13,372 +15,20 @@ use crate::function_meta::{ }; use crate::item::IntoComponent; use crate::macros::{MacroContext, TokenStream}; -use crate::module::{ - AssociatedKey, Async, EnumMut, Function, FunctionKind, InstallWith, InstanceFunction, - InternalEnum, InternalEnumMut, ItemFnMut, ItemMut, ModuleAssociated, ModuleAssociatedKind, - ModuleAttributeMacro, ModuleFunction, ModuleItem, ModuleItemCommon, ModuleItemKind, - ModuleMacro, ModuleType, Plain, TypeMut, TypeSpecification, VariantMut, -}; +use crate::module::DocFunction; use crate::runtime::{ - AttributeMacroHandler, ConstValue, FromValue, FunctionHandler, GeneratorState, InstAddress, - MacroHandler, MaybeTypeOf, Output, Protocol, Stack, ToValue, TypeCheck, TypeInfo, TypeOf, - Value, VmResult, + ConstValue, FromValue, GeneratorState, InstAddress, MaybeTypeOf, Memory, Output, Protocol, + ToValue, TypeCheck, TypeOf, Value, VmResult, +}; +use crate::{Hash, Item, ItemBuf}; + +use super::{ + AssociatedKey, EnumMut, InstallWith, InternalEnum, InternalEnumMut, ItemFnMut, ItemMut, + ModuleAssociated, ModuleAssociatedKind, ModuleAttributeMacro, ModuleConstantBuilder, + ModuleFunction, ModuleFunctionBuilder, ModuleItem, ModuleItemCommon, ModuleItemKind, + ModuleMacro, ModuleMeta, ModuleRawFunctionBuilder, ModuleReexport, ModuleTrait, + ModuleTraitImpl, ModuleType, TraitMut, TypeMut, TypeSpecification, VariantMut, }; -use crate::{Hash, ItemBuf}; - -/// Function builder as returned by [`Module::function`]. -/// -/// This allows for building a function regularly with -/// [`ModuleFunctionBuilder::build`] or statically associate the function with a -/// type through [`ModuleFunctionBuilder::build_associated::`]. -#[must_use = "Must call one of the build functions, like `build` or `build_associated`"] -pub struct ModuleFunctionBuilder<'a, F, A, N, K> { - module: &'a mut Module, - inner: FunctionBuilder, -} - -impl<'a, F, A, N, K> ModuleFunctionBuilder<'a, F, A, N, K> -where - F: Function, - F::Return: MaybeTypeOf, - A: FunctionArgs, - K: FunctionKind, -{ - /// Construct a regular function. - /// - /// This register the function as a free function in the module it's - /// associated with, who's full name is the name of the module extended by - /// the name of the function. - /// - /// # Examples - /// - /// ``` - /// use rune::{Any, Module}; - /// - /// let mut m = Module::with_item(["module"])?; - /// m.function("floob", || ()).build()?; - /// # Ok::<_, rune::support::Error>(()) - /// ``` - #[inline] - pub fn build(self) -> Result, ContextError> - where - N: IntoComponent, - { - let meta = self.inner.build()?; - self.module.function_from_meta_kind(meta) - } - - /// Construct a function that is associated with `T`. - /// - /// This registers the function as an assocaited function, which can only be - /// used through the type `T`. - /// - /// # Errors - /// - /// This function call will cause an error in [`Context::install`] if the - /// type we're associating it with has not been registered. - /// - /// [`Context::install`]: crate::Context::install - /// - /// ``` - /// use rune::{Any, Context, Module}; - /// - /// #[derive(Any)] - /// struct Thing; - /// - /// let mut m = Module::default(); - /// m.function("floob", || ()).build_associated::()?; - /// - /// let mut c = Context::default(); - /// assert!(c.install(m).is_err()); - /// # Ok::<_, rune::support::Error>(()) - /// ``` - /// - /// # Examples - /// - /// ``` - /// use rune::{Any, Module}; - /// - /// #[derive(Any)] - /// struct Thing; - /// - /// let mut m = Module::default(); - /// m.ty::()?; - /// m.function("floob", || ()).build_associated::()?; - /// # Ok::<_, rune::support::Error>(()) - /// ``` - #[inline] - pub fn build_associated(self) -> Result, ContextError> - where - N: ToInstance, - T: TypeOf, - { - let meta = self.inner.build_associated::()?; - self.module.function_from_meta_kind(meta) - } - - /// Construct a function that is associated with a custom dynamically - /// specified container. - /// - /// This registers the function as an assocaited function, which can only be - /// used through the specified type. - /// - /// [`Hash`] and [`TypeInfo`] are usually constructed through the - /// [`TypeOf`] trait. But that requires access to a static type, for which - /// you should use [`build_associated`] instead. - /// - /// # Errors - /// - /// The function call will error if the specified type is not already - /// registered in the module. - /// - /// [`build_associated`]: ModuleFunctionBuilder::build_associated - #[inline] - pub fn build_associated_with( - self, - container: Hash, - container_type_info: TypeInfo, - ) -> Result, ContextError> - where - N: ToInstance, - { - let meta = self - .inner - .build_associated_with(container, container_type_info)?; - self.module.function_from_meta_kind(meta) - } -} - -/// Raw function builder as returned by [`Module::raw_function`]. -/// -/// This allows for building a function regularly with -/// [`ModuleRawFunctionBuilder::build`] or statically associate the function -/// with a type through [`ModuleRawFunctionBuilder::build_associated::`]. -#[must_use = "Must call one of the build functions, like `build` or `build_associated`"] -pub struct ModuleRawFunctionBuilder<'a, N> { - module: &'a mut Module, - name: N, - handler: Arc, -} - -impl<'a, N> ModuleRawFunctionBuilder<'a, N> { - /// Construct a regular function. - /// - /// This register the function as a free function in the module it's - /// associated with, who's full name is the name of the module extended by - /// the name of the function. - /// - /// # Examples - /// - /// ``` - /// use rune::{Any, Module}; - /// use rune::runtime::VmResult; - /// - /// let mut m = Module::with_item(["module"])?; - /// m.raw_function("floob", |_, _, _, _| VmResult::Ok(())).build()?; - /// # Ok::<_, rune::support::Error>(()) - /// ``` - #[inline] - pub fn build(self) -> Result, ContextError> - where - N: IntoComponent, - { - let item = ItemBuf::with_item([self.name])?; - self.module - .function_from_meta_kind(FunctionMetaKind::Function(FunctionData::from_raw( - item, - self.handler, - ))) - } - - /// Construct a function that is associated with `T`. - /// - /// This registers the function as an assocaited function, which can only be - /// used through the type `T`. - /// - /// # Errors - /// - /// This function call will cause an error in [`Context::install`] if the - /// type we're associating it with has not been registered. - /// - /// [`Context::install`]: crate::Context::install - /// - /// ``` - /// use rune::{Any, Module, Context}; - /// - /// #[derive(Any)] - /// struct Thing; - /// - /// let mut m = Module::default(); - /// m.function("floob", || ()).build_associated::()?; - /// - /// let mut c = Context::default(); - /// assert!(c.install(m).is_err()); - /// # Ok::<_, rune::support::Error>(()) - /// ``` - /// - /// # Examples - /// - /// ``` - /// use rune::{Any, Module}; - /// use rune::runtime::VmResult; - /// - /// #[derive(Any)] - /// struct Thing; - /// - /// let mut m = Module::default(); - /// m.ty::()?; - /// m.raw_function("floob", |_, _, _, _| VmResult::Ok(())).build_associated::()?; - /// # Ok::<_, rune::support::Error>(()) - /// ``` - #[inline] - pub fn build_associated(self) -> Result, ContextError> - where - N: ToInstance, - T: TypeOf, - { - let associated = Associated::from_type::(self.name.to_instance()?)?; - - self.module - .function_from_meta_kind(FunctionMetaKind::AssociatedFunction( - AssociatedFunctionData::from_raw(associated, self.handler), - )) - } - - /// Construct a function that is associated with a custom dynamically - /// specified container. - /// - /// This registers the function as an assocaited function, which can only be - /// used through the specified type. - /// - /// [`Hash`] and [`TypeInfo`] are usually constructed through the - /// [`TypeOf`] trait. But that requires access to a static type, for which - /// you should use [`build_associated`] instead. - /// - /// # Errors - /// - /// The function call will error if the specified type is not already - /// registered in the module. - /// - /// [`build_associated`]: ModuleFunctionBuilder::build_associated - #[inline] - pub fn build_associated_with( - self, - container: Hash, - container_type_info: TypeInfo, - ) -> Result, ContextError> - where - N: ToInstance, - { - let associated = Associated::new(self.name.to_instance()?, container, container_type_info); - self.module - .function_from_meta_kind(FunctionMetaKind::AssociatedFunction( - AssociatedFunctionData::from_raw(associated, self.handler), - )) - } -} - -/// Raw function builder as returned by [`Module::raw_function`]. -/// -/// This allows for building a function regularly with -/// [`ModuleConstantBuilder::build`] or statically associate the function with a -/// type through [`ModuleConstantBuilder::build_associated::`]. -#[must_use = "Must call one of the build functions, like `build` or `build_associated`"] -pub struct ModuleConstantBuilder<'a, N, V> { - module: &'a mut Module, - name: N, - value: V, -} - -impl<'a, N, V> ModuleConstantBuilder<'a, N, V> -where - V: ToValue, -{ - /// Add the free constant directly to the module. - /// - /// # Examples - /// - /// ``` - /// use rune::{Any, Module}; - /// use rune::runtime::VmResult; - /// - /// let mut m = Module::with_item(["module"])?; - /// m.constant("NAME", "Hello World").build()?; - /// # Ok::<_, rune::support::Error>(()) - /// ``` - pub fn build(self) -> Result, ContextError> - where - N: IntoComponent, - { - let item = ItemBuf::with_item([self.name])?; - self.module.insert_constant(item, self.value) - } - - /// Build a constant that is associated with the static type `T`. - /// - /// # Errors - /// - /// This function call will cause an error in [`Context::install`] if the - /// type we're associating it with has not been registered. - /// - /// [`Context::install`]: crate::Context::install - /// - /// ``` - /// use rune::{Any, Context, Module}; - /// - /// #[derive(Any)] - /// struct Thing; - /// - /// let mut m = Module::default(); - /// m.constant("CONSTANT", "Hello World").build_associated::()?; - /// - /// let mut c = Context::default(); - /// assert!(c.install(m).is_err()); - /// # Ok::<_, rune::support::Error>(()) - /// ``` - /// - /// # Examples - /// - /// ``` - /// use rune::{Any, Module}; - /// - /// let mut module = Module::default(); - /// - /// #[derive(Any)] - /// struct Thing; - /// - /// module.constant("TEN", 10).build_associated::()?.docs(["Ten which is an associated constant."]); - /// # Ok::<_, rune::support::Error>(()) - /// ``` - pub fn build_associated(self) -> Result, ContextError> - where - T: TypeOf, - N: ToInstance, - { - let name = self.name.to_instance()?; - let associated = Associated::from_type::(name)?; - self.module - .insert_associated_constant(associated, self.value) - } -} - -#[doc(hidden)] -pub struct ModuleMetaData { - #[doc(hidden)] - pub item: ItemBuf, - #[doc(hidden)] - pub docs: &'static [&'static str], -} - -/// Type used to collect and store module metadata through the `#[rune::module]` -/// macro. -/// -/// This is the argument type for [`Module::from_meta`], and is from a public -/// API perspective completely opaque and might change for any release. -/// -/// Calling and making use of `ModuleMeta` manually despite this warning might -/// lead to future breakage. -pub type ModuleMeta = fn() -> alloc::Result; #[derive(Debug, TryClone, PartialEq, Eq, Hash)] enum Name { @@ -390,6 +40,8 @@ enum Name { Macro(Hash), /// An attribute macro. AttributeMacro(Hash), + /// A conflicting trait implementation. + TraitImpl(Hash, Hash), } /// A [Module] that is a collection of native functions and types. @@ -412,6 +64,12 @@ pub struct Module { pub(crate) types: Vec, /// Type hash to types mapping. pub(crate) types_hash: HashMap, + /// A trait registered in the current module. + pub(crate) traits: Vec, + /// A trait implementation registered in the current module. + pub(crate) trait_impls: Vec, + /// A re-export in the current module. + pub(crate) reexports: Vec, /// Module level metadata. pub(crate) common: ModuleItemCommon, } @@ -434,11 +92,7 @@ impl Module { } /// Construct a new module for the given item. - pub fn with_item(iter: I) -> Result - where - I: IntoIterator, - I::Item: IntoComponent, - { + pub fn with_item(iter: impl IntoIterator) -> Result { Ok(Self::inner_new(ItemBuf::with_item(iter)?)) } @@ -448,11 +102,10 @@ impl Module { } /// Construct a new module for the given crate. - pub fn with_crate_item(name: &str, iter: I) -> Result - where - I: IntoIterator, - I::Item: IntoComponent, - { + pub fn with_crate_item( + name: &str, + iter: impl IntoIterator, + ) -> Result { Ok(Self::inner_new(ItemBuf::with_crate_item(name, iter)?)) } @@ -472,7 +125,10 @@ impl Module { items: Vec::new(), associated: Vec::new(), types: Vec::new(), + traits: Vec::new(), + trait_impls: Vec::new(), types_hash: HashMap::new(), + reexports: Vec::new(), common: ModuleItemCommon { docs: Docs::EMPTY, deprecated: None, @@ -552,11 +208,11 @@ impl Module { self.types.try_push(ModuleType { item, + hash, common: ModuleItemCommon { docs: Docs::EMPTY, deprecated: None, }, - hash, type_parameters, type_info, spec: None, @@ -737,6 +393,7 @@ impl Module { self.install_internal_enum(name, enum_) } + /// Construct type information for the `Option` type. /// /// Registering this allows the given type to be used in Rune scripts when @@ -844,7 +501,9 @@ impl Module { /// struct MyType; /// /// module.constant("TEN", 10).build()?.docs(["A global ten value."]); - /// module.constant("TEN", 10).build_associated::()?.docs(["Ten which looks like an associated constant."]); + /// module.constant("TEN", 10).build_associated::()?.docs(rune::docstring! { + /// /// Ten which looks like an associated constant. + /// }); /// # Ok::<_, rune::support::Error>(()) /// ``` pub fn constant(&mut self, name: N, value: V) -> ModuleConstantBuilder<'_, N, V> @@ -858,7 +517,11 @@ impl Module { } } - fn insert_constant(&mut self, item: ItemBuf, value: V) -> Result, ContextError> + pub(super) fn insert_constant( + &mut self, + item: ItemBuf, + value: V, + ) -> Result, ContextError> where V: ToValue, { @@ -896,7 +559,7 @@ impl Module { }) } - fn insert_associated_constant( + pub(super) fn insert_associated_constant( &mut self, associated: Associated, value: V, @@ -1172,7 +835,7 @@ impl Module { /// Register a function handler through its meta. /// /// The metadata must be provided by annotating the function with - /// [`#[rune::function]`][crate::function]. + /// [`#[rune::function]`][macro@crate::function]. /// /// This has the benefit that it captures documentation comments which can /// be used when generating documentation or referencing the function @@ -1257,7 +920,7 @@ impl Module { } } - fn function_from_meta_kind( + pub(super) fn function_from_meta_kind( &mut self, kind: FunctionMetaKind, ) -> Result, ContextError> { @@ -1670,10 +1333,10 @@ impl Module { /// /// ``` /// use rune::Module; - /// use rune::runtime::{Output, Stack, ToValue, VmResult, InstAddress}; + /// use rune::runtime::{Output, Memory, ToValue, VmResult, InstAddress}; /// use rune::vm_try; /// - /// fn sum(stack: &mut Stack, addr: InstAddress, args: usize, out: Output) -> VmResult<()> { + /// fn sum(stack: &mut dyn Memory, addr: InstAddress, args: usize, out: Output) -> VmResult<()> { /// let mut number = 0; /// /// for value in vm_try!(stack.slice_at(addr, args)) { @@ -1696,7 +1359,7 @@ impl Module { /// ``` pub fn raw_function(&mut self, name: N, f: F) -> ModuleRawFunctionBuilder<'_, N> where - F: 'static + Fn(&mut Stack, InstAddress, usize, Output) -> VmResult<()> + Send + Sync, + F: 'static + Fn(&mut dyn Memory, InstAddress, usize, Output) -> VmResult<()> + Send + Sync, { ModuleRawFunctionBuilder { module: self, @@ -1709,7 +1372,7 @@ impl Module { #[deprecated = "Use `raw_function` builder instead"] pub fn raw_fn(&mut self, name: N, f: F) -> Result, ContextError> where - F: 'static + Fn(&mut Stack, InstAddress, usize, Output) -> VmResult<()> + Send + Sync, + F: 'static + Fn(&mut dyn Memory, InstAddress, usize, Output) -> VmResult<()> + Send + Sync, N: IntoComponent, { self.raw_function(name, f).build() @@ -1735,14 +1398,17 @@ impl Module { common: ModuleItemCommon { docs, deprecated }, kind: ModuleItemKind::Function(ModuleFunction { handler: data.handler, - #[cfg(feature = "doc")] - is_async: data.is_async, - #[cfg(feature = "doc")] - args: data.args, - #[cfg(feature = "doc")] - return_type: data.return_type, - #[cfg(feature = "doc")] - argument_types: data.argument_types, + trait_hash: None, + doc: DocFunction { + #[cfg(feature = "doc")] + is_async: data.is_async, + #[cfg(feature = "doc")] + args: data.args, + #[cfg(feature = "doc")] + return_type: data.return_type, + #[cfg(feature = "doc")] + argument_types: data.argument_types, + }, }), })?; @@ -1759,13 +1425,13 @@ impl Module { #[cfg(feature = "doc")] deprecated: &mut last.common.deprecated, #[cfg(feature = "doc")] - is_async: &mut last_fn.is_async, + is_async: &mut last_fn.doc.is_async, #[cfg(feature = "doc")] - args: &mut last_fn.args, + args: &mut last_fn.doc.args, #[cfg(feature = "doc")] - return_type: &mut last_fn.return_type, + return_type: &mut last_fn.doc.return_type, #[cfg(feature = "doc")] - argument_types: &mut last_fn.argument_types, + argument_types: &mut last_fn.doc.argument_types, }) } @@ -1785,14 +1451,17 @@ impl Module { common: ModuleItemCommon { docs, deprecated }, kind: ModuleAssociatedKind::Function(ModuleFunction { handler: data.handler, - #[cfg(feature = "doc")] - is_async: data.is_async, - #[cfg(feature = "doc")] - args: data.args, - #[cfg(feature = "doc")] - return_type: data.return_type, - #[cfg(feature = "doc")] - argument_types: data.argument_types, + trait_hash: None, + doc: DocFunction { + #[cfg(feature = "doc")] + is_async: data.is_async, + #[cfg(feature = "doc")] + args: data.args, + #[cfg(feature = "doc")] + return_type: data.return_type, + #[cfg(feature = "doc")] + argument_types: data.argument_types, + }, }), })?; @@ -1809,13 +1478,13 @@ impl Module { #[cfg(feature = "doc")] deprecated: &mut last.common.deprecated, #[cfg(feature = "doc")] - is_async: &mut last_fn.is_async, + is_async: &mut last_fn.doc.is_async, #[cfg(feature = "doc")] - args: &mut last_fn.args, + args: &mut last_fn.doc.args, #[cfg(feature = "doc")] - return_type: &mut last_fn.return_type, + return_type: &mut last_fn.doc.return_type, #[cfg(feature = "doc")] - argument_types: &mut last_fn.argument_types, + argument_types: &mut last_fn.doc.argument_types, }) } @@ -1854,6 +1523,93 @@ impl Module { Ok(()) } + + /// Define a new trait. + pub fn define_trait( + &mut self, + item: impl IntoIterator, + ) -> Result, ContextError> { + let item = self.item.join(item)?; + let hash = Hash::type_hash(&item); + + if !self.names.try_insert(Name::Item(hash))? { + return Err(ContextError::ConflictingTrait { item, hash }); + } + + self.traits.try_push(ModuleTrait { + item, + hash, + common: ModuleItemCommon::default(), + handler: None, + functions: Vec::new(), + })?; + + let t = self.traits.last_mut().unwrap(); + + Ok(TraitMut { + docs: &mut t.common.docs, + #[cfg(feature = "doc")] + deprecated: &mut t.common.deprecated, + handler: &mut t.handler, + functions: &mut t.functions, + }) + } + + /// Implement the trait `trait_item` for the type `T`. + pub fn implement_trait(&mut self, trait_item: &Item) -> Result<(), ContextError> + where + T: ?Sized + TypeOf + Named, + { + let item = ItemBuf::with_item([T::BASE_NAME])?; + let hash = T::type_hash(); + let type_info = T::type_info(); + let trait_hash = Hash::type_hash(trait_item); + + if !self.names.try_insert(Name::TraitImpl(hash, trait_hash))? { + return Err(ContextError::ConflictingTraitImpl { + trait_item: trait_item.try_to_owned()?, + trait_hash, + item, + hash, + }); + } + + self.trait_impls.try_push(ModuleTraitImpl { + item, + hash, + type_info, + trait_item: trait_item.try_to_owned()?, + trait_hash, + })?; + + Ok(()) + } + + /// Define a re-export. + pub fn reexport( + &mut self, + item: impl IntoIterator, + to: &Item, + ) -> Result<(), ContextError> { + let item = self.item.join(item)?; + let hash = Hash::type_hash(&item); + + if !self.names.try_insert(Name::Item(hash))? { + return Err(ContextError::ConflictingReexport { + item, + hash, + to: to.try_to_owned()?, + }); + } + + self.reexports.try_push(ModuleReexport { + item, + hash, + to: to.try_to_owned()?, + })?; + + Ok(()) + } } impl AsRef for Module { diff --git a/crates/rune/src/module/module_constant_builder.rs b/crates/rune/src/module/module_constant_builder.rs new file mode 100644 index 000000000..409a71ef4 --- /dev/null +++ b/crates/rune/src/module/module_constant_builder.rs @@ -0,0 +1,92 @@ +use crate::compile::ContextError; +use crate::function_meta::{Associated, ToInstance}; +use crate::item::IntoComponent; +use crate::module::ItemMut; +use crate::runtime::{ToValue, TypeOf}; +use crate::ItemBuf; + +use super::Module; + +/// Raw function builder as returned by [`Module::raw_function`]. +/// +/// This allows for building a function regularly with +/// [`ModuleConstantBuilder::build`] or statically associate the function with a +/// type through [`ModuleConstantBuilder::build_associated::`]. +#[must_use = "Must call one of the build functions, like `build` or `build_associated`"] +pub struct ModuleConstantBuilder<'a, N, V> { + pub(super) module: &'a mut Module, + pub(super) name: N, + pub(super) value: V, +} + +impl<'a, N, V> ModuleConstantBuilder<'a, N, V> +where + V: ToValue, +{ + /// Add the free constant directly to the module. + /// + /// # Examples + /// + /// ``` + /// use rune::{Any, Module}; + /// use rune::runtime::VmResult; + /// + /// let mut m = Module::with_item(["module"])?; + /// m.constant("NAME", "Hello World").build()?; + /// # Ok::<_, rune::support::Error>(()) + /// ``` + pub fn build(self) -> Result, ContextError> + where + N: IntoComponent, + { + let item = ItemBuf::with_item([self.name])?; + self.module.insert_constant(item, self.value) + } + + /// Build a constant that is associated with the static type `T`. + /// + /// # Errors + /// + /// This function call will cause an error in [`Context::install`] if the + /// type we're associating it with has not been registered. + /// + /// [`Context::install`]: crate::Context::install + /// + /// ``` + /// use rune::{Any, Context, Module}; + /// + /// #[derive(Any)] + /// struct Thing; + /// + /// let mut m = Module::default(); + /// m.constant("CONSTANT", "Hello World").build_associated::()?; + /// + /// let mut c = Context::default(); + /// assert!(c.install(m).is_err()); + /// # Ok::<_, rune::support::Error>(()) + /// ``` + /// + /// # Examples + /// + /// ``` + /// use rune::{Any, Module}; + /// + /// let mut module = Module::default(); + /// + /// #[derive(Any)] + /// struct Thing; + /// + /// module.constant("TEN", 10).build_associated::()?.docs(["Ten which is an associated constant."]); + /// # Ok::<_, rune::support::Error>(()) + /// ``` + pub fn build_associated(self) -> Result, ContextError> + where + T: TypeOf, + N: ToInstance, + { + let name = self.name.to_instance()?; + let associated = Associated::from_type::(name)?; + self.module + .insert_associated_constant(associated, self.value) + } +} diff --git a/crates/rune/src/module/module_function_builder.rs b/crates/rune/src/module/module_function_builder.rs new file mode 100644 index 000000000..285b9899f --- /dev/null +++ b/crates/rune/src/module/module_function_builder.rs @@ -0,0 +1,133 @@ +use crate::compile::ContextError; +use crate::function::{Function, FunctionKind}; +use crate::function_meta::{FunctionArgs, FunctionBuilder, ToInstance}; +use crate::item::IntoComponent; +use crate::module::ItemFnMut; +use crate::runtime::{MaybeTypeOf, TypeInfo, TypeOf}; +use crate::Hash; + +use super::Module; + +/// Function builder as returned by [`Module::function`]. +/// +/// This allows for building a function regularly with +/// [`ModuleFunctionBuilder::build`] or statically associate the function with a +/// type through [`ModuleFunctionBuilder::build_associated::`]. +#[must_use = "Must call one of the build functions, like `build` or `build_associated`"] +pub struct ModuleFunctionBuilder<'a, F, A, N, K> { + pub(super) module: &'a mut Module, + pub(super) inner: FunctionBuilder, +} + +impl<'a, F, A, N, K> ModuleFunctionBuilder<'a, F, A, N, K> +where + F: Function, + F::Return: MaybeTypeOf, + A: FunctionArgs, + K: FunctionKind, +{ + /// Construct a regular function. + /// + /// This register the function as a free function in the module it's + /// associated with, who's full name is the name of the module extended by + /// the name of the function. + /// + /// # Examples + /// + /// ``` + /// use rune::{Any, Module}; + /// + /// let mut m = Module::with_item(["module"])?; + /// m.function("floob", || ()).build()?; + /// # Ok::<_, rune::support::Error>(()) + /// ``` + #[inline] + pub fn build(self) -> Result, ContextError> + where + N: IntoComponent, + { + let meta = self.inner.build()?; + self.module.function_from_meta_kind(meta) + } + + /// Construct a function that is associated with `T`. + /// + /// This registers the function as an assocaited function, which can only be + /// used through the type `T`. + /// + /// # Errors + /// + /// This function call will cause an error in [`Context::install`] if the + /// type we're associating it with has not been registered. + /// + /// [`Context::install`]: crate::Context::install + /// + /// ``` + /// use rune::{Any, Context, Module}; + /// + /// #[derive(Any)] + /// struct Thing; + /// + /// let mut m = Module::default(); + /// m.function("floob", || ()).build_associated::()?; + /// + /// let mut c = Context::default(); + /// assert!(c.install(m).is_err()); + /// # Ok::<_, rune::support::Error>(()) + /// ``` + /// + /// # Examples + /// + /// ``` + /// use rune::{Any, Module}; + /// + /// #[derive(Any)] + /// struct Thing; + /// + /// let mut m = Module::default(); + /// m.ty::()?; + /// m.function("floob", || ()).build_associated::()?; + /// # Ok::<_, rune::support::Error>(()) + /// ``` + #[inline] + pub fn build_associated(self) -> Result, ContextError> + where + N: ToInstance, + T: TypeOf, + { + let meta = self.inner.build_associated::()?; + self.module.function_from_meta_kind(meta) + } + + /// Construct a function that is associated with a custom dynamically + /// specified container. + /// + /// This registers the function as an assocaited function, which can only be + /// used through the specified type. + /// + /// [`Hash`] and [`TypeInfo`] are usually constructed through the + /// [`TypeOf`] trait. But that requires access to a static type, for which + /// you should use [`build_associated`] instead. + /// + /// # Errors + /// + /// The function call will error if the specified type is not already + /// registered in the module. + /// + /// [`build_associated`]: ModuleFunctionBuilder::build_associated + /// [`Hash`]: crate::Hash + #[inline] + pub fn build_associated_with( + self, + container: Hash, + container_type_info: TypeInfo, + ) -> Result, ContextError> + where + N: ToInstance, + { + let meta = self + .inner + .build_associated_with(container, container_type_info)?; + self.module.function_from_meta_kind(meta) + } +} diff --git a/crates/rune/src/module/module_meta.rs b/crates/rune/src/module/module_meta.rs new file mode 100644 index 000000000..7c44111da --- /dev/null +++ b/crates/rune/src/module/module_meta.rs @@ -0,0 +1,233 @@ +use core::fmt; + +use ::rust_alloc::sync::Arc; + +use crate as rune; +use crate::alloc; +use crate::alloc::prelude::*; +use crate::compile::context::{AttributeMacroHandler, MacroHandler, TraitHandler}; +use crate::compile::{meta, Docs}; +use crate::function_meta::AssociatedName; +use crate::runtime::{ConstValue, FunctionHandler, TypeCheck, TypeInfo}; +use crate::{Hash, ItemBuf}; + +use super::InternalEnum; + +#[doc(hidden)] +pub struct ModuleMetaData { + #[doc(hidden)] + pub item: ItemBuf, + #[doc(hidden)] + pub docs: &'static [&'static str], +} + +/// Type used to collect and store module metadata through the `#[rune::module]` +/// macro. +/// +/// This is the argument type for [`Module::from_meta`], and is from a public +/// API perspective completely opaque and might change for any release. +/// +/// Calling and making use of `ModuleMeta` manually despite this warning might +/// lead to future breakage. +/// +/// [`Module::from_meta`]: crate::Module::from_meta +pub type ModuleMeta = fn() -> alloc::Result; + +/// Data for an opaque type. If `spec` is set, indicates things which are known +/// about that type. +pub(crate) struct ModuleType { + /// The name of the installed type which will be the final component in the + /// item it will constitute. + pub(crate) item: ItemBuf, + /// Type hash of the type. + pub(crate) hash: Hash, + /// Common item metadata. + pub(crate) common: ModuleItemCommon, + /// Type parameters for this item. + pub(crate) type_parameters: Hash, + /// Type information for the installed type. + pub(crate) type_info: TypeInfo, + /// The specification for the type. + pub(crate) spec: Option, + /// Handler to use if this type can be constructed through a regular function call. + pub(crate) constructor: Option>, +} + +/// A trait defined in a module. +pub(crate) struct ModuleTrait { + pub(crate) item: ItemBuf, + pub(crate) hash: Hash, + pub(crate) common: ModuleItemCommon, + pub(crate) handler: Option>, + pub(crate) functions: Vec, +} + +/// A type implementing a trait. +pub(crate) struct ModuleTraitImpl { + pub(crate) item: ItemBuf, + pub(crate) hash: Hash, + pub(crate) type_info: TypeInfo, + pub(crate) trait_item: ItemBuf, + pub(crate) trait_hash: Hash, +} + +/// A reexport of an item. +pub(crate) struct ModuleReexport { + pub(crate) item: ItemBuf, + pub(crate) hash: Hash, + pub(crate) to: ItemBuf, +} + +/// The kind of the variant. +#[derive(Debug)] +pub(crate) enum Fields { + /// Sequence of named fields. + Named(&'static [&'static str]), + /// Sequence of unnamed fields. + Unnamed(usize), + /// Empty. + Empty, +} + +/// Metadata about a variant. +pub struct Variant { + /// The name of the variant. + pub(crate) name: &'static str, + /// Type check for the variant. + pub(crate) type_check: Option, + /// Variant metadata. + pub(crate) fields: Option, + /// Handler to use if this variant can be constructed through a regular function call. + pub(crate) constructor: Option>, + /// Variant deprecation. + pub(crate) deprecated: Option>, + /// Variant documentation. + pub(crate) docs: Docs, +} + +impl Variant { + pub(super) fn new(name: &'static str) -> Self { + Self { + name, + type_check: None, + fields: None, + constructor: None, + deprecated: None, + docs: Docs::EMPTY, + } + } +} + +impl fmt::Debug for Variant { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut f = f.debug_struct("Variant"); + f.field("fields", &self.fields); + f.field("constructor", &self.constructor.is_some()); + #[cfg(feature = "doc")] + f.field("deprecated", &self.deprecated); + f.field("docs", &self.docs); + f.finish() + } +} + +/// The type specification for a native enum. +pub(crate) struct Enum { + /// The variants. + pub(crate) variants: Vec, +} + +/// A type specification. +pub(crate) enum TypeSpecification { + Struct(Fields), + Enum(Enum), +} + +/// A key that identifies an associated function. +#[derive(Debug, TryClone, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub(crate) struct AssociatedKey { + /// The type the associated function belongs to. + pub(crate) type_hash: Hash, + /// The kind of the associated function. + pub(crate) kind: meta::AssociatedKind, + /// The type parameters of the associated function. + pub(crate) parameters: Hash, +} + +pub(crate) enum ModuleItemKind { + Constant(ConstValue), + Function(ModuleFunction), + Macro(ModuleMacro), + AttributeMacro(ModuleAttributeMacro), + InternalEnum(InternalEnum), +} + +pub(crate) struct ModuleItem { + pub(crate) item: ItemBuf, + pub(crate) common: ModuleItemCommon, + pub(crate) kind: ModuleItemKind, +} + +#[derive(Default, TryClone)] +pub(crate) struct DocFunction { + #[cfg(feature = "doc")] + #[try_clone(copy)] + pub(crate) is_async: bool, + #[cfg(feature = "doc")] + #[try_clone(copy)] + pub(crate) args: Option, + #[cfg(feature = "doc")] + pub(crate) argument_types: Box<[meta::DocType]>, + #[cfg(feature = "doc")] + pub(crate) return_type: meta::DocType, +} + +#[derive(TryClone)] +pub(crate) struct ModuleFunction { + /// The handler for the function. + pub(crate) handler: Arc, + /// If the function is associated with a trait, this is the hash of that trait. + pub(crate) trait_hash: Option, + /// Documentation related to the function. + pub(crate) doc: DocFunction, +} + +#[derive(TryClone)] +pub(crate) enum ModuleAssociatedKind { + Constant(ConstValue), + Function(ModuleFunction), +} + +#[derive(Default, TryClone)] +pub(crate) struct ModuleItemCommon { + /// Documentation for the item. + pub(crate) docs: Docs, + /// Deprecation marker for the item. + pub(crate) deprecated: Option>, +} + +#[derive(TryClone)] +pub(crate) struct ModuleAssociated { + pub(crate) container: Hash, + pub(crate) container_type_info: TypeInfo, + pub(crate) name: AssociatedName, + pub(crate) common: ModuleItemCommon, + pub(crate) kind: ModuleAssociatedKind, +} + +/// Handle to a macro inserted into a module. +pub(crate) struct ModuleMacro { + pub(crate) handler: Arc, +} + +/// Handle to an attribute macro inserted into a module. +pub(crate) struct ModuleAttributeMacro { + pub(crate) handler: Arc, +} + +/// Handle to a trait function inserted into a module. +pub(crate) struct TraitFunction { + pub(crate) name: AssociatedName, + pub(crate) common: ModuleItemCommon, + pub(crate) doc: DocFunction, +} diff --git a/crates/rune/src/module/module_raw_function_builder.rs b/crates/rune/src/module/module_raw_function_builder.rs new file mode 100644 index 000000000..a0a6ed181 --- /dev/null +++ b/crates/rune/src/module/module_raw_function_builder.rs @@ -0,0 +1,142 @@ +use ::rust_alloc::sync::Arc; + +use crate::compile::ContextError; +use crate::function_meta::{ + Associated, AssociatedFunctionData, FunctionData, FunctionMetaKind, ToInstance, +}; +use crate::item::IntoComponent; +use crate::module::ItemFnMut; +use crate::runtime::{FunctionHandler, TypeInfo, TypeOf}; +use crate::{Hash, ItemBuf}; + +use super::Module; + +/// Raw function builder as returned by [`Module::raw_function`]. +/// +/// This allows for building a function regularly with +/// [`ModuleRawFunctionBuilder::build`] or statically associate the function +/// with a type through [`ModuleRawFunctionBuilder::build_associated::`]. +#[must_use = "Must call one of the build functions, like `build` or `build_associated`"] +pub struct ModuleRawFunctionBuilder<'a, N> { + pub(super) module: &'a mut Module, + pub(super) name: N, + pub(super) handler: Arc, +} + +impl<'a, N> ModuleRawFunctionBuilder<'a, N> { + /// Construct a regular function. + /// + /// This register the function as a free function in the module it's + /// associated with, who's full name is the name of the module extended by + /// the name of the function. + /// + /// # Examples + /// + /// ``` + /// use rune::{Any, Module}; + /// use rune::runtime::VmResult; + /// + /// let mut m = Module::with_item(["module"])?; + /// m.raw_function("floob", |_, _, _, _| VmResult::Ok(())).build()?; + /// # Ok::<_, rune::support::Error>(()) + /// ``` + #[inline] + pub fn build(self) -> Result, ContextError> + where + N: IntoComponent, + { + let item = ItemBuf::with_item([self.name])?; + self.module + .function_from_meta_kind(FunctionMetaKind::Function(FunctionData::from_raw( + item, + self.handler, + ))) + } + + /// Construct a function that is associated with `T`. + /// + /// This registers the function as an assocaited function, which can only be + /// used through the type `T`. + /// + /// # Errors + /// + /// This function call will cause an error in [`Context::install`] if the + /// type we're associating it with has not been registered. + /// + /// [`Context::install`]: crate::Context::install + /// + /// ``` + /// use rune::{Any, Module, Context}; + /// + /// #[derive(Any)] + /// struct Thing; + /// + /// let mut m = Module::default(); + /// m.function("floob", || ()).build_associated::()?; + /// + /// let mut c = Context::default(); + /// assert!(c.install(m).is_err()); + /// # Ok::<_, rune::support::Error>(()) + /// ``` + /// + /// # Examples + /// + /// ``` + /// use rune::{Any, Module}; + /// use rune::runtime::VmResult; + /// + /// #[derive(Any)] + /// struct Thing; + /// + /// let mut m = Module::default(); + /// m.ty::()?; + /// m.raw_function("floob", |_, _, _, _| VmResult::Ok(())).build_associated::()?; + /// # Ok::<_, rune::support::Error>(()) + /// ``` + #[inline] + pub fn build_associated(self) -> Result, ContextError> + where + N: ToInstance, + T: TypeOf, + { + let associated = Associated::from_type::(self.name.to_instance()?)?; + + self.module + .function_from_meta_kind(FunctionMetaKind::AssociatedFunction( + AssociatedFunctionData::from_raw(associated, self.handler), + )) + } + + /// Construct a function that is associated with a custom dynamically + /// specified container. + /// + /// This registers the function as an assocaited function, which can only be + /// used through the specified type. + /// + /// [`Hash`] and [`TypeInfo`] are usually constructed through the [`TypeOf`] + /// trait. But that requires access to a static type, for which you should + /// use [`build_associated`] instead. + /// + /// # Errors + /// + /// The function call will error if the specified type is not already + /// registered in the module. + /// + /// [`Hash`]: crate::Hash + /// [`build_associated`]: super::ModuleFunctionBuilder::build_associated + #[inline] + pub fn build_associated_with( + self, + container: Hash, + container_type_info: TypeInfo, + ) -> Result, ContextError> + where + N: ToInstance, + { + let associated = Associated::new(self.name.to_instance()?, container, container_type_info); + self.module + .function_from_meta_kind(FunctionMetaKind::AssociatedFunction( + AssociatedFunctionData::from_raw(associated, self.handler), + )) + } +} diff --git a/crates/rune/src/module/trait_mut.rs b/crates/rune/src/module/trait_mut.rs new file mode 100644 index 000000000..abd81c750 --- /dev/null +++ b/crates/rune/src/module/trait_mut.rs @@ -0,0 +1,106 @@ +use core::fmt; + +use ::rust_alloc::sync::Arc; + +#[cfg(feature = "doc")] +use crate::alloc::Box; +use crate::alloc::Vec; +use crate::compile::context::{TraitContext, TraitHandler}; +use crate::compile::{ContextError, Docs}; +use crate::function_meta::ToInstance; + +use super::{DocFunction, ItemFnMut, ModuleItemCommon, TraitFunction}; + +/// Handle to a a trait inserted into a module which allows for mutation of its +/// metadata. +pub struct TraitMut<'a> { + pub(super) docs: &'a mut Docs, + #[cfg(feature = "doc")] + pub(super) deprecated: &'a mut Option>, + pub(super) handler: &'a mut Option>, + pub(super) functions: &'a mut Vec, +} + +impl TraitMut<'_> { + /// Set documentation for an inserted trait. + /// + /// This completely replaces any existing documentation. + pub fn docs(&mut self, docs: I) -> Result<&mut Self, ContextError> + where + I: IntoIterator, + I::Item: AsRef, + { + self.docs.set_docs(docs)?; + Ok(self) + } + + /// Set static documentation. + /// + /// This completely replaces any existing documentation. + pub fn static_docs( + &mut self, + docs: &'static [&'static str], + ) -> Result<&mut Self, ContextError> { + self.docs.set_docs(docs)?; + Ok(self) + } + + /// Mark the given trait as deprecated. + pub fn deprecated( + &mut self, + #[cfg_attr(not(feature = "doc"), allow(unused))] deprecated: S, + ) -> Result<&mut Self, ContextError> + where + S: AsRef, + { + #[cfg(feature = "doc")] + { + *self.deprecated = Some(deprecated.as_ref().try_into()?); + } + + Ok(self) + } + + /// Define a trait handler. + pub fn handler(&mut self, handler: F) -> Result<&mut Self, ContextError> + where + F: 'static + Fn(&mut TraitContext<'_>) -> Result<(), ContextError> + Send + Sync, + { + *self.handler = Some(Arc::new(handler)); + Ok(self) + } + + /// Define a function on the trait. + pub fn function(&mut self, name: impl ToInstance) -> Result, ContextError> { + let name = name.to_instance()?; + + self.functions.try_push(TraitFunction { + name, + common: ModuleItemCommon::default(), + doc: DocFunction::default(), + })?; + + let f = self.functions.last_mut().unwrap(); + + Ok(ItemFnMut { + docs: &mut f.common.docs, + #[cfg(feature = "doc")] + deprecated: &mut f.common.deprecated, + #[cfg(feature = "doc")] + is_async: &mut f.doc.is_async, + #[cfg(feature = "doc")] + args: &mut f.doc.args, + #[cfg(feature = "doc")] + return_type: &mut f.doc.return_type, + #[cfg(feature = "doc")] + argument_types: &mut f.doc.argument_types, + }) + } +} + +impl fmt::Debug for TraitMut<'_> { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TraitMut").finish_non_exhaustive() + } +} diff --git a/crates/rune/src/module/type_mut.rs b/crates/rune/src/module/type_mut.rs new file mode 100644 index 000000000..ef1e49379 --- /dev/null +++ b/crates/rune/src/module/type_mut.rs @@ -0,0 +1,159 @@ +use core::fmt; +use core::marker::PhantomData; + +use ::rust_alloc::sync::Arc; + +use crate::alloc::prelude::*; +use crate::compile::{ContextError, Docs}; +use crate::function::{Function, Plain}; +use crate::runtime::{FunctionHandler, TypeOf}; +use crate::Item; + +use super::{Enum, EnumMut, Fields, TypeSpecification, Variant}; + +/// Handle to a a type inserted into a module which allows for mutation of its +/// metadata. +/// +/// This is returned by the following methods: +/// * [`Module::ty`] - after a type has been inserted. +/// * [`Module::type_meta`] - to modify type metadata for an already inserted +/// type. +/// +/// [`Module::ty`]: super::Module::ty +/// [`Module::type_meta`]: super::Module::type_meta +pub struct TypeMut<'a, T> +where + T: ?Sized + TypeOf, +{ + pub(super) docs: &'a mut Docs, + #[cfg(feature = "doc")] + pub(super) deprecated: &'a mut Option>, + pub(super) spec: &'a mut Option, + pub(super) constructor: &'a mut Option>, + pub(super) item: &'a Item, + pub(super) _marker: PhantomData, +} + +impl<'a, T> TypeMut<'a, T> +where + T: ?Sized + TypeOf, +{ + /// Set documentation for an inserted type. + /// + /// This completely replaces any existing documentation. + pub fn docs(self, docs: I) -> Result + where + I: IntoIterator, + I::Item: AsRef, + { + self.docs.set_docs(docs)?; + Ok(self) + } + + /// Set static documentation. + /// + /// This completely replaces any existing documentation. + pub fn static_docs(self, docs: &'static [&'static str]) -> Result { + self.docs.set_docs(docs)?; + Ok(self) + } + + /// Mark the given type as deprecated. + pub fn deprecated( + self, + #[cfg_attr(not(feature = "doc"), allow(unused))] deprecated: S, + ) -> Result + where + S: AsRef, + { + #[cfg(feature = "doc")] + { + *self.deprecated = Some(deprecated.as_ref().try_into()?); + } + + Ok(self) + } + + /// Mark the current type as a struct with named fields. + pub fn make_named_struct(self, fields: &'static [&'static str]) -> Result { + self.make_struct(Fields::Named(fields)) + } + + /// Mark the current type as a struct with unnamed fields. + pub fn make_unnamed_struct(self, fields: usize) -> Result { + self.make_struct(Fields::Unnamed(fields)) + } + + /// Mark the current type as an empty struct. + pub fn make_empty_struct(self) -> Result { + self.make_struct(Fields::Empty) + } + + /// Mark the current type as an enum. + pub fn make_enum( + self, + variants: &'static [&'static str], + ) -> Result, ContextError> { + let old = self.spec.replace(TypeSpecification::Enum(Enum { + variants: variants.iter().copied().map(Variant::new).try_collect()?, + })); + + if old.is_some() { + return Err(ContextError::ConflictingTypeMeta { + item: self.item.try_to_owned()?, + type_info: T::type_info(), + }); + } + + let Some(TypeSpecification::Enum(enum_)) = self.spec.as_mut() else { + panic!("Not an enum"); + }; + + Ok(EnumMut { + docs: self.docs, + enum_, + _marker: PhantomData, + }) + } + + /// Register a constructor method for the current type. + pub fn constructor(self, constructor: F) -> Result + where + F: Function, + { + if self.constructor.is_some() { + return Err(ContextError::ConstructorConflict { + type_info: T::type_info(), + }); + } + + *self.constructor = Some(Arc::new(move |stack, addr, args, output| { + constructor.fn_call(stack, addr, args, output) + })); + + Ok(self) + } + + fn make_struct(self, fields: Fields) -> Result { + let old = self.spec.replace(TypeSpecification::Struct(fields)); + + if old.is_some() { + return Err(ContextError::ConflictingTypeMeta { + item: self.item.try_to_owned()?, + type_info: T::type_info(), + }); + } + + Ok(self) + } +} + +impl fmt::Debug for TypeMut<'_, T> +where + T: TypeOf, +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TypeMut").finish_non_exhaustive() + } +} diff --git a/crates/rune/src/module/variant_mut.rs b/crates/rune/src/module/variant_mut.rs new file mode 100644 index 000000000..59d16c1d0 --- /dev/null +++ b/crates/rune/src/module/variant_mut.rs @@ -0,0 +1,94 @@ +use core::marker::PhantomData; + +use ::rust_alloc::sync::Arc; + +use crate::compile::{ContextError, Docs}; +use crate::function::{Function, Plain}; +use crate::runtime::{FunctionHandler, TypeOf}; + +use super::Fields; + +/// Handle to a a variant inserted into a module which allows for mutation of +/// its metadata. +pub struct VariantMut<'a, T> +where + T: ?Sized + TypeOf, +{ + pub(crate) index: usize, + pub(crate) docs: &'a mut Docs, + pub(crate) fields: &'a mut Option, + pub(crate) constructor: &'a mut Option>, + pub(crate) _marker: PhantomData, +} + +impl VariantMut<'_, T> +where + T: ?Sized + TypeOf, +{ + /// Set documentation for an inserted type. + /// + /// This completely replaces any existing documentation. + pub fn docs(self, docs: I) -> Result + where + I: IntoIterator, + I::Item: AsRef, + { + self.docs.set_docs(docs)?; + Ok(self) + } + + /// Set static documentation. + /// + /// This completely replaces any existing documentation. + pub fn static_docs(self, docs: &'static [&'static str]) -> Result { + self.docs.set_docs(docs)?; + Ok(self) + } + + /// Mark the given variant with named fields. + pub fn make_named(self, fields: &'static [&'static str]) -> Result { + self.make(Fields::Named(fields)) + } + + /// Mark the given variant with unnamed fields. + pub fn make_unnamed(self, fields: usize) -> Result { + self.make(Fields::Unnamed(fields)) + } + + /// Mark the given variant as empty. + pub fn make_empty(self) -> Result { + self.make(Fields::Empty) + } + + /// Register a constructor method for the current variant. + pub fn constructor(self, constructor: F) -> Result + where + F: Function, + { + if self.constructor.is_some() { + return Err(ContextError::VariantConstructorConflict { + type_info: T::type_info(), + index: self.index, + }); + } + + *self.constructor = Some(Arc::new(move |stack, addr, args, output| { + constructor.fn_call(stack, addr, args, output) + })); + + Ok(self) + } + + fn make(self, fields: Fields) -> Result { + let old = self.fields.replace(fields); + + if old.is_some() { + return Err(ContextError::ConflictingVariantMeta { + index: self.index, + type_info: T::type_info(), + }); + } + + Ok(self) + } +} diff --git a/crates/rune/src/modules/bytes.rs b/crates/rune/src/modules/bytes.rs index 8489c41b0..0880ae49f 100644 --- a/crates/rune/src/modules/bytes.rs +++ b/crates/rune/src/modules/bytes.rs @@ -9,27 +9,30 @@ use crate::{ContextError, Module}; /// The bytes module. #[rune::module(::std::bytes)] pub fn module() -> Result { - let mut module = Module::from_meta(self::module_meta)?; + let mut m = Module::from_meta(self::module_meta)?; - module.ty::()?; - module.function_meta(new)?; - module.function_meta(with_capacity)?; - module.function_meta(from_vec)?; - module.function_meta(into_vec)?; - module.function_meta(as_vec)?; - module.function_meta(extend)?; - module.function_meta(extend_str)?; - module.function_meta(pop)?; - module.function_meta(last)?; - module.function_meta(len)?; - module.function_meta(is_empty)?; - module.function_meta(capacity)?; - module.function_meta(clear)?; - module.function_meta(reserve)?; - module.function_meta(reserve_exact)?; - module.function_meta(clone)?; - module.function_meta(shrink_to_fit)?; - Ok(module) + m.ty::()?; + m.function_meta(new)?; + m.function_meta(with_capacity)?; + m.function_meta(from_vec)?; + m.function_meta(into_vec)?; + m.function_meta(as_vec)?; + m.function_meta(extend)?; + m.function_meta(extend_str)?; + m.function_meta(pop)?; + m.function_meta(last)?; + m.function_meta(len)?; + m.function_meta(is_empty)?; + m.function_meta(capacity)?; + m.function_meta(clear)?; + m.function_meta(reserve)?; + m.function_meta(reserve_exact)?; + m.function_meta(shrink_to_fit)?; + + m.function_meta(clone__meta)?; + m.implement_trait::(rune::item!(::std::clone::Clone))?; + + Ok(m) } /// Construct a new byte array. @@ -309,7 +312,7 @@ fn reserve_exact(this: &mut Bytes, additional: usize) -> VmResult<()> { /// assert_eq!(a, b"hello world!"); /// assert_eq!(b, b"hello world"); /// ``` -#[rune::function(instance)] +#[rune::function(keep, instance, protocol = CLONE)] fn clone(this: &Bytes) -> VmResult { VmResult::Ok(vm_try!(this.try_clone())) } diff --git a/crates/rune/src/modules/capture_io.rs b/crates/rune/src/modules/capture_io.rs index 29e3e05e0..ebc464f8d 100644 --- a/crates/rune/src/modules/capture_io.rs +++ b/crates/rune/src/modules/capture_io.rs @@ -23,7 +23,7 @@ use crate as rune; use crate::alloc::fmt::TryWrite; use crate::alloc::string::FromUtf8Error; use crate::alloc::{String, Vec}; -use crate::runtime::{InstAddress, Output, Stack, VmError, VmResult}; +use crate::runtime::{InstAddress, Memory, Output, VmError, VmResult}; use crate::{ContextError, Module, Value}; /// I/O module capable of capturing what's been written to a buffer. @@ -108,7 +108,7 @@ impl CaptureIo { fn dbg_impl( o: &mut Vec, - stack: &mut Stack, + stack: &mut dyn Memory, addr: InstAddress, args: usize, out: Output, diff --git a/crates/rune/src/modules/clone.rs b/crates/rune/src/modules/clone.rs index 3b4ebb499..127df9071 100644 --- a/crates/rune/src/modules/clone.rs +++ b/crates/rune/src/modules/clone.rs @@ -1,20 +1,63 @@ //! The cloning trait for Rune. use crate as rune; -use crate::runtime::{Value, VmResult}; +use crate::runtime::{Protocol, Value, VmResult}; use crate::{ContextError, Module}; -/// The cloning trait for Rune. +/// Cloning for Rune. /// /// This module defines methods and types used when cloning values. /// /// By default all values in rune are structurally shared, so in order to get a -/// unique instance of it you must call [`clone`] over it. +/// unique instance of it you must clone it. #[rune::module(::std::clone)] pub fn module() -> Result { - let mut module = Module::from_meta(self::module_meta)?; - module.function_meta(clone)?; - Ok(module) + let mut m = Module::from_meta(self::module_meta)?; + m.function_meta(clone)?; + + let mut t = m.define_trait(["Clone"])?; + + t.docs(docstring! { + /// The `Clone` trait is used to explicitly clone values. + /// + /// # Examples + /// + /// ```rune + /// let a = 42; + /// let b = a.clone(); + /// + /// assert_eq!(a, b); + /// ``` + })?; + + t.handler(|cx| { + let clone = cx.find(Protocol::CLONE)?; + cx.function_handler("clone", &clone)?; + Ok(()) + })?; + + t.function("clone")? + .argument_types::<(Value,)>()? + .return_type::()? + .docs(docstring! { + /// Clone the specified `value`. + /// + /// # Examples + /// + /// ```rune + /// let a = 42; + /// let b = a; + /// let c = a.clone(); + /// + /// a += 1; + /// + /// assert_eq!(a, 43); + /// assert_eq!(b, 43); + /// assert_eq!(c, 42); + /// ``` + })?; + + Ok(m) } /// Clone the specified `value`. diff --git a/crates/rune/src/modules/cmp.rs b/crates/rune/src/modules/cmp.rs index 971eba75c..0a8978334 100644 --- a/crates/rune/src/modules/cmp.rs +++ b/crates/rune/src/modules/cmp.rs @@ -4,7 +4,8 @@ use core::cmp::Ordering; use crate as rune; use crate::alloc::fmt::TryWrite; -use crate::runtime::{Formatter, Value, VmResult}; +use crate::runtime::{Formatter, Protocol, Value, VmResult}; +use crate::shared::Caller; use crate::{ContextError, Module}; /// Comparison and ordering. @@ -13,49 +14,200 @@ pub fn module() -> Result { let mut m = Module::from_meta(self::module_meta)?; { - let ty = m.ty::()?.docs([ - "An `Ordering` is the result of a comparison between two values.", - "", - "# Examples", - "", - "```rune", - "use std::cmp::Ordering;", - "use std::ops::cmp;", - "", - "let result = cmp(1, 2);", - "assert_eq!(Ordering::Less, result);", - "", - "let result = cmp(1, 1);", - "assert_eq!(Ordering::Equal, result);", - "", - "let result = cmp(2, 1);", - "assert_eq!(Ordering::Greater, result);", - "```", - ])?; + let ty = m.ty::()?.docs(docstring! { + /// An `Ordering` is the result of a comparison between two values. + /// + /// # Examples + /// + /// ```rune + /// use std::cmp::Ordering; + /// use std::ops::cmp; + /// + /// let result = 1.cmp(2); + /// assert_eq!(Ordering::Less, result); + /// + /// let result = 1.cmp(1); + /// assert_eq!(Ordering::Equal, result); + /// + /// let result = 2.cmp(1); + /// assert_eq!(Ordering::Greater, result); + /// ``` + })?; let mut ty = ty.make_enum(&["Less", "Equal", "Greater"])?; ty.variant_mut(0)? .make_empty()? .constructor(|| Ordering::Less)? - .docs(["An ordering where a compared value is less than another."])?; + .docs(docstring! { + /// "An ordering where a compared value is less than another. + })?; ty.variant_mut(1)? .make_empty()? .constructor(|| Ordering::Equal)? - .docs(["An ordering where a compared value is equal to another."])?; + .docs(docstring! { + /// "An ordering where a compared value is equal to another. + })?; ty.variant_mut(2)? .make_empty()? .constructor(|| Ordering::Greater)? - .docs(["An ordering where a compared value is greater than another."])?; + .docs(docstring! { + /// "An ordering where a compared value is greater than another. + })?; } - m.function_meta(ordering_partial_eq)?; - m.function_meta(ordering_eq)?; + m.function_meta(ordering_partial_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + + m.function_meta(ordering_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + m.function_meta(ordering_string_debug)?; m.function_meta(min)?; m.function_meta(max)?; + + let mut t = m.define_trait(["PartialEq"])?; + + t.docs(docstring! { + /// Trait to perform a partial equality check over two values. + /// + /// This produces the same behavior as the equality operator (`==`). + /// + /// For non-builtin types this leans on the behavior of the [`PARTIAL_EQ`] + /// protocol. + /// + /// # Panics + /// + /// Panics if we're trying to compare two values which are not comparable. + /// + /// # Examples + /// + /// ```rune + /// assert!((1.0).eq(1.0)); + /// assert!(!(1.0).eq(2.0)); + /// + /// assert!(1.0 == 1.0); + /// assert!(1.0 != 2.0); + /// ``` + })?; + + t.handler(|cx| { + let partial_eq = cx.find(Protocol::PARTIAL_EQ)?; + cx.function_handler("eq", &partial_eq)?; + + let partial_eq = Caller::::new(partial_eq); + + cx.function("ne", move |a: Value, b: Value| { + VmResult::Ok(!vm_try!(partial_eq.call((a, b)))) + })?; + + Ok(()) + })?; + + t.function("eq")? + .argument_types::<(Value, Value)>()? + .return_type::()? + .docs(docstring! { + /// Compare two values for equality. + /// + /// # Examples + /// + /// ```rune + /// assert_eq!(1.eq(2), false); + /// assert_eq!(2.eq(2), true); + /// assert_eq!(2.eq(1), false); + /// ``` + })?; + + t.function("ne")? + .argument_types::<(Value, Value)>()? + .return_type::()? + .docs(docstring! { + /// Compare two values for inequality. + /// + /// # Examples + /// + /// ```rune + /// assert_eq!(1.ne(2), true); + /// assert_eq!(2.ne(2), false); + /// assert_eq!(2.ne(1), true); + /// ``` + })?; + + let mut t = m.define_trait(["Eq"])?; + + t.handler(|cx| { + _ = cx.find(Protocol::EQ)?; + Ok(()) + })?; + + t.docs(docstring! { + /// Trait for equality comparisons. + /// + /// This trait allows for comparing whether two values are equal or not. + /// + /// # Examples + /// + /// ```rune + /// use std::cmp::Eq; + /// + /// assert!(1.eq(1)); + /// assert!(!1.eq(2)); + /// ``` + })?; + + let mut t = m.define_trait(["PartialOrd"])?; + + t.handler(|cx| { + let partial_cmp = cx.find(Protocol::PARTIAL_CMP)?; + cx.function_handler("partial_cmp", &partial_cmp)?; + Ok(()) + })?; + + t.function("partial_cmp")? + .argument_types::<(Value, Value)>()? + .return_type::>()? + .docs(docstring! { + /// Compare two values. + /// + /// # Examples + /// + /// ```rune + /// use std::cmp::Ordering; + /// + /// assert_eq!(1.partial_cmp(2), Some(Ordering::Less)); + /// assert_eq!(2.partial_cmp(2), Some(Ordering::Equal)); + /// assert_eq!(2.partial_cmp(1), Some(Ordering::Greater)); + /// ``` + })?; + + let mut t = m.define_trait(["Ord"])?; + + t.handler(|cx| { + let cmp = cx.find(Protocol::CMP)?; + cx.function_handler("cmp", &cmp)?; + Ok(()) + })?; + + t.function("cmp")? + .argument_types::<(Value, Value)>()? + .return_type::()? + .docs(docstring! { + /// Compare two values. + /// + /// # Examples + /// + /// ```rune + /// use std::cmp::Ordering; + /// + /// assert_eq!(1.cmp(2), Ordering::Less); + /// assert_eq!(2.cmp(2), Ordering::Equal); + /// assert_eq!(2.cmp(1), Ordering::Greater); + /// ``` + })?; + Ok(m) } @@ -113,7 +265,7 @@ fn min(v1: Value, v2: Value) -> VmResult { /// assert!(Ordering::Less == Ordering::Less); /// assert!(Ordering::Less != Ordering::Equal); /// ``` -#[rune::function(instance, protocol = PARTIAL_EQ)] +#[rune::function(keep, instance, protocol = PARTIAL_EQ)] fn ordering_partial_eq(this: Ordering, other: Ordering) -> bool { this == other } @@ -129,7 +281,7 @@ fn ordering_partial_eq(this: Ordering, other: Ordering) -> bool { /// assert!(eq(Ordering::Less, Ordering::Less)); /// assert!(!eq(Ordering::Less, Ordering::Equal)); /// ``` -#[rune::function(instance, protocol = EQ)] +#[rune::function(keep, instance, protocol = EQ)] fn ordering_eq(this: Ordering, other: Ordering) -> bool { this == other } diff --git a/crates/rune/src/modules/collections.rs b/crates/rune/src/modules/collections.rs deleted file mode 100644 index 5695a59a8..000000000 --- a/crates/rune/src/modules/collections.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Dynamic collections. - -#[cfg(feature = "alloc")] -mod hash_map; -#[cfg(feature = "alloc")] -mod hash_set; -mod vec_deque; - -use crate::{ContextError, Module}; - -#[cfg(feature = "alloc")] -pub(crate) use self::hash_map::HashMap; -#[cfg(feature = "alloc")] -pub(crate) use self::hash_set::HashSet; -pub(crate) use self::vec_deque::VecDeque; -use crate as rune; - -/// Dynamic collections. -#[rune::module(::std::collections)] -pub fn module() -> Result { - let mut module = Module::from_meta(self::module_meta)?; - #[cfg(feature = "alloc")] - hash_map::setup(&mut module)?; - #[cfg(feature = "alloc")] - hash_set::setup(&mut module)?; - vec_deque::setup(&mut module)?; - Ok(module) -} diff --git a/crates/rune/src/modules/collections/hash_map.rs b/crates/rune/src/modules/collections/hash_map.rs index f072d64e1..1c2461054 100644 --- a/crates/rune/src/modules/collections/hash_map.rs +++ b/crates/rune/src/modules/collections/hash_map.rs @@ -1,42 +1,68 @@ use crate as rune; use crate::alloc::fmt::TryWrite; use crate::alloc::prelude::*; -use crate::hashbrown::Table; +use crate::hashbrown::{IterRef, KeysRef, Table, ValuesRef}; use crate::runtime::{ EnvProtocolCaller, Formatter, FromValue, Iterator, ProtocolCaller, Ref, Value, VmErrorKind, VmResult, }; use crate::{Any, ContextError, Module}; -pub(super) fn setup(module: &mut Module) -> Result<(), ContextError> { - module.ty::()?; - module.function_meta(HashMap::new__meta)?; - module.function_meta(HashMap::with_capacity__meta)?; - module.function_meta(HashMap::len__meta)?; - module.function_meta(HashMap::capacity__meta)?; - module.function_meta(HashMap::insert__meta)?; - module.function_meta(HashMap::get__meta)?; - module.function_meta(HashMap::contains_key__meta)?; - module.function_meta(HashMap::remove__meta)?; - module.function_meta(HashMap::clear__meta)?; - module.function_meta(HashMap::is_empty__meta)?; - module.function_meta(HashMap::iter__meta)?; - module.function_meta(HashMap::keys__meta)?; - module.function_meta(HashMap::values__meta)?; - module.function_meta(HashMap::extend__meta)?; - module.function_meta(HashMap::from__meta)?; - module.function_meta(HashMap::clone__meta)?; - module.function_meta(HashMap::index_set__meta)?; - module.function_meta(HashMap::index_get__meta)?; - module.function_meta(HashMap::string_debug__meta)?; - module.function_meta(HashMap::partial_eq__meta)?; - module.function_meta(HashMap::eq__meta)?; - module.function_meta(HashMap::into_iter__meta)?; - Ok(()) +/// A dynamic hash map. +#[rune::module(::std::collections::hash_map)] +pub fn module() -> Result { + let mut m = Module::from_meta(self::module_meta)?; + + m.ty::()?; + m.function_meta(HashMap::new__meta)?; + m.function_meta(HashMap::with_capacity__meta)?; + m.function_meta(HashMap::len__meta)?; + m.function_meta(HashMap::capacity__meta)?; + m.function_meta(HashMap::insert__meta)?; + m.function_meta(HashMap::get__meta)?; + m.function_meta(HashMap::contains_key__meta)?; + m.function_meta(HashMap::remove__meta)?; + m.function_meta(HashMap::clear__meta)?; + m.function_meta(HashMap::is_empty__meta)?; + m.function_meta(HashMap::iter__meta)?; + m.function_meta(HashMap::into_iter__meta)?; + m.function_meta(HashMap::from_iter__meta)?; + m.function_meta(HashMap::keys__meta)?; + m.function_meta(HashMap::values__meta)?; + m.function_meta(HashMap::extend__meta)?; + m.function_meta(HashMap::index_set__meta)?; + m.function_meta(HashMap::index_get__meta)?; + m.function_meta(HashMap::string_debug__meta)?; + + m.function_meta(HashMap::clone__meta)?; + m.implement_trait::(rune::item!(::std::clone::Clone))?; + + m.function_meta(HashMap::partial_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + + m.function_meta(HashMap::eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + + m.ty::()?; + m.function_meta(Iter::next)?; + m.function_meta(Iter::size_hint)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + + m.ty::()?; + m.function_meta(Keys::next)?; + m.function_meta(Keys::size_hint)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + + m.ty::()?; + m.function_meta(Values::next)?; + m.function_meta(Values::size_hint)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + + Ok(m) } #[derive(Any)] -#[rune(item = ::std::collections)] +#[rune(item = ::std::collections::hash_map)] pub(crate) struct HashMap { table: Table, } @@ -73,7 +99,7 @@ impl HashMap { /// let map = HashMap::with_capacity(10); /// ``` #[rune::function(keep, path = Self::with_capacity)] - fn with_capacity(capacity: usize) -> VmResult { + pub(crate) fn with_capacity(capacity: usize) -> VmResult { VmResult::Ok(Self { table: vm_try!(Table::try_with_capacity(capacity)), }) @@ -155,7 +181,7 @@ impl HashMap { /// assert_eq!(map[37], "c"); /// ``` #[rune::function(keep)] - fn insert(&mut self, key: Value, value: Value) -> VmResult> { + pub(crate) fn insert(&mut self, key: Value, value: Value) -> VmResult> { let mut caller = EnvProtocolCaller; self.table.insert_with(key, value, &mut caller) } @@ -240,7 +266,7 @@ impl HashMap { /// ```rune /// use std::collections::HashMap; /// - /// let map = HashMap::from([ + /// let map = HashMap::from_iter([ /// ("a", 1), /// ("b", 2), /// ("c", 3), @@ -256,9 +282,9 @@ impl HashMap { /// In the current implementation, iterating over map takes O(capacity) time /// instead of O(len) because it internally visits empty buckets too. #[rune::function(keep, instance, path = Self::iter)] - fn iter(this: Ref) -> Iterator { + fn iter(this: Ref) -> Iter { let iter = Table::iter_ref(Ref::map(this, |this| &this.table)); - Iterator::from("std::collections::hash_map::Iter", iter) + Iter { iter } } /// An iterator visiting all keys in arbitrary order. @@ -268,7 +294,7 @@ impl HashMap { /// ```rune /// use std::collections::HashMap; /// - /// let map = HashMap::from([ + /// let map = HashMap::from_iter([ /// ("a", 1), /// ("b", 2), /// ("c", 3), @@ -284,9 +310,10 @@ impl HashMap { /// In the current implementation, iterating over keys takes O(capacity) /// time instead of O(len) because it internally visits empty buckets too. #[rune::function(keep, instance, path = Self::keys)] - fn keys(this: Ref) -> Iterator { + fn keys(this: Ref) -> Keys { let iter = Table::keys_ref(Ref::map(this, |this| &this.table)); - Iterator::from("std::collections::hash_map::Keys", iter) + + Keys { iter } } /// An iterator visiting all values in arbitrary order. @@ -296,7 +323,7 @@ impl HashMap { /// ```rune /// use std::collections::HashMap; /// - /// let map = HashMap::from([ + /// let map = HashMap::from_iter([ /// ("a", 1), /// ("b", 2), /// ("c", 3), @@ -312,10 +339,10 @@ impl HashMap { /// In the current implementation, iterating over values takes O(capacity) /// time instead of O(len) because it internally visits empty buckets too. #[rune::function(keep, instance, path = Self::values)] - fn values(this: Ref) -> Iterator { + fn values(this: Ref) -> Values { let iter = Table::values_ref(Ref::map(this, |this| &this.table)); - Iterator::from("std::collections::hash_map::Values", iter) + Values { iter } } /// Extend this map from an iterator. @@ -345,16 +372,6 @@ impl HashMap { VmResult::Ok(()) } - /// Convert a hashmap from a `value`. - /// - /// The hashmap can be converted from anything that implements the [`INTO_ITER`] - /// protocol, and each item produces should be a tuple pair. - #[rune::function(keep, path = Self::from)] - fn from(value: Value) -> VmResult { - let mut caller = EnvProtocolCaller; - HashMap::from_iter(vm_try!(value.into_iter()), &mut caller) - } - /// Clone the map. /// /// # Examples @@ -362,7 +379,11 @@ impl HashMap { /// ```rune /// use std::collections::HashMap; /// - /// let a = HashMap::from([("a", 1), ("b", 2)]); + /// let a = HashMap::from_iter([ + /// ("a", 1), + /// ("b", 2), + /// ]); + /// /// let b = a.clone(); /// /// b.insert("c", 3); @@ -370,14 +391,38 @@ impl HashMap { /// assert_eq!(a.len(), 2); /// assert_eq!(b.len(), 3); /// ``` - #[rune::function(keep, instance, path = Self::clone)] + #[rune::function(keep, instance, path = Self::clone, protocol = CLONE)] fn clone(this: &HashMap) -> VmResult { VmResult::Ok(Self { table: vm_try!(this.table.try_clone()), }) } - pub(crate) fn from_iter(mut it: Iterator, caller: &mut dyn ProtocolCaller) -> VmResult { + /// Convert a hashmap from a value convert into an iterator. + /// + /// The hashmap can be converted from anything that implements the + /// [`INTO_ITER`] protocol, and each item produces should be a tuple pair. + /// + /// # Examples + /// + /// ```rune + /// use std::collections::HashMap; + /// + /// let map = HashMap::from_iter([("a", 1), ("b", 2)]); + /// assert_eq!(map.len(), 2); + /// assert_eq!(map.get("a"), Some(1)); + /// assert_eq!(map.get("b"), Some(2)); + /// ``` + #[rune::function(keep, path = Self::from_iter)] + fn from_iter(it: Iterator) -> VmResult { + let mut caller = EnvProtocolCaller; + Self::from_iter_with(it, &mut caller) + } + + pub(crate) fn from_iter_with( + mut it: Iterator, + caller: &mut dyn ProtocolCaller, + ) -> VmResult { let mut map = Self::new(); while let Some(value) = vm_try!(it.next()) { @@ -496,13 +541,13 @@ impl HashMap { /// ```rune /// use std::collections::HashMap; /// - /// let map1 = HashMap::from([ + /// let map1 = HashMap::from_iter([ /// ("a", 1.0), /// ("c", 3.0), /// ("b", 2.0), /// ]); /// - /// let map2 = HashMap::from([ + /// let map2 = HashMap::from_iter([ /// ("c", 3.0), /// ("a", 1.0), /// ("b", 2.0), @@ -546,13 +591,13 @@ impl HashMap { /// use std::collections::HashMap; /// use std::ops::eq; /// - /// let map1 = HashMap::from([ + /// let map1 = HashMap::from_iter([ /// ("a", 1), /// ("c", 3), /// ("b", 2), /// ]); /// - /// let map2 = HashMap::from([ + /// let map2 = HashMap::from_iter([ /// ("c", 3), /// ("a", 1), /// ("b", 2), @@ -590,7 +635,7 @@ impl HashMap { /// ```rune /// use std::collections::HashMap; /// - /// let map = HashMap::from([ + /// let map = HashMap::from_iter([ /// ("a", 1), /// ("b", 2), /// ("c", 3), @@ -611,7 +656,64 @@ impl HashMap { /// In the current implementation, iterating over map takes O(capacity) time /// instead of O(len) because it internally visits empty buckets too. #[rune::function(keep, instance, protocol = INTO_ITER, path = Self)] - fn into_iter(this: Ref) -> Iterator { + fn into_iter(this: Ref) -> Iter { Self::iter(this) } } + +/// An iterator over a hash map. +#[derive(Any)] +#[rune(item = ::std::collections::hash_map)] +pub(crate) struct Iter { + iter: IterRef, +} + +impl Iter { + #[rune::function(instance, protocol = NEXT)] + fn next(&mut self) -> Option<(Value, Value)> { + self.iter.next() + } + + #[rune::function(instance, protocol = SIZE_HINT)] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +/// An iterator over a the keys in a hash map. +#[derive(Any)] +#[rune(item = ::std::collections::hash_map)] +pub(crate) struct Keys { + iter: KeysRef, +} + +impl Keys { + #[rune::function(instance, protocol = NEXT)] + fn next(&mut self) -> Option { + self.iter.next() + } + + #[rune::function(instance, protocol = SIZE_HINT)] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +/// An iterator over a the values in a hash map. +#[derive(Any)] +#[rune(item = ::std::collections::hash_map)] +pub(crate) struct Values { + iter: ValuesRef, +} + +impl Values { + #[rune::function(instance, protocol = NEXT)] + fn next(&mut self) -> Option { + self.iter.next() + } + + #[rune::function(instance, protocol = SIZE_HINT)] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} diff --git a/crates/rune/src/modules/collections/hash_set.rs b/crates/rune/src/modules/collections/hash_set.rs index 4d735f824..3f6ecb368 100644 --- a/crates/rune/src/modules/collections/hash_set.rs +++ b/crates/rune/src/modules/collections/hash_set.rs @@ -1,9 +1,7 @@ -use crate::alloc::fmt::TryWrite; -use core::iter; use core::ptr; use crate as rune; - +use crate::alloc::fmt::TryWrite; use crate::alloc::hashbrown::raw::RawIter; use crate::alloc::prelude::*; use crate::hashbrown::{IterRef, Table}; @@ -12,33 +10,64 @@ use crate::runtime::{ }; use crate::{Any, ContextError, Module}; -pub(super) fn setup(module: &mut Module) -> Result<(), ContextError> { - module.ty::()?; - module.function_meta(HashSet::new__meta)?; - module.function_meta(HashSet::with_capacity__meta)?; - module.function_meta(HashSet::len__meta)?; - module.function_meta(HashSet::is_empty__meta)?; - module.function_meta(HashSet::capacity__meta)?; - module.function_meta(HashSet::insert__meta)?; - module.function_meta(HashSet::remove__meta)?; - module.function_meta(HashSet::contains__meta)?; - module.function_meta(HashSet::clear__meta)?; - module.function_meta(HashSet::difference__meta)?; - module.function_meta(HashSet::extend__meta)?; - module.function_meta(HashSet::intersection__meta)?; - module.function_meta(HashSet::union__meta)?; - module.function_meta(HashSet::iter__meta)?; - module.function_meta(HashSet::into_iter__meta)?; - module.function_meta(HashSet::string_debug__meta)?; - module.function_meta(HashSet::partial_eq__meta)?; - module.function_meta(HashSet::eq__meta)?; - module.function_meta(HashSet::clone__meta)?; - module.function_meta(HashSet::from__meta)?; - Ok(()) +/// A dynamic hash set. +#[rune::module(::std::collections::hash_set)] +pub fn module() -> Result { + let mut m = Module::from_meta(self::module_meta)?; + + m.ty::()?; + m.function_meta(HashSet::new__meta)?; + m.function_meta(HashSet::with_capacity__meta)?; + m.function_meta(HashSet::len__meta)?; + m.function_meta(HashSet::is_empty__meta)?; + m.function_meta(HashSet::capacity__meta)?; + m.function_meta(HashSet::insert__meta)?; + m.function_meta(HashSet::remove__meta)?; + m.function_meta(HashSet::contains__meta)?; + m.function_meta(HashSet::clear__meta)?; + m.function_meta(HashSet::difference__meta)?; + m.function_meta(HashSet::extend__meta)?; + m.function_meta(HashSet::intersection__meta)?; + m.function_meta(HashSet::union__meta)?; + m.function_meta(HashSet::iter__meta)?; + m.function_meta(HashSet::into_iter__meta)?; + m.function_meta(HashSet::from_iter__meta)?; + m.function_meta(HashSet::string_debug__meta)?; + + m.function_meta(HashSet::clone__meta)?; + m.implement_trait::(rune::item!(::std::clone::Clone))?; + + m.function_meta(HashSet::partial_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + + m.function_meta(HashSet::eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + + m.ty::()?; + m.function_meta(Iter::next__meta)?; + m.function_meta(Iter::size_hint__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.function_meta(Iter::len__meta)?; + m.implement_trait::(rune::item!(::std::iter::ExactSizeIterator))?; + + m.ty::()?; + m.function_meta(Difference::next__meta)?; + m.function_meta(Difference::size_hint__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + + m.ty::()?; + m.function_meta(Intersection::next__meta)?; + m.function_meta(Intersection::size_hint__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + + m.ty::()?; + m.function_meta(Union::next__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + Ok(m) } #[derive(Any)] -#[rune(module = crate, item = ::std::collections)] +#[rune(module = crate, item = ::std::collections::hash_set)] pub(crate) struct HashSet { table: Table<()>, } @@ -78,7 +107,7 @@ impl HashSet { /// assert!(set.capacity() >= 10); /// ``` #[rune::function(keep, path = Self::with_capacity)] - fn with_capacity(capacity: usize) -> VmResult { + pub(crate) fn with_capacity(capacity: usize) -> VmResult { VmResult::Ok(Self { table: vm_try!(Table::try_with_capacity(capacity)), }) @@ -152,7 +181,7 @@ impl HashSet { /// assert_eq!(set.len(), 1); /// ``` #[rune::function(keep)] - fn insert(&mut self, key: Value) -> VmResult { + pub(crate) fn insert(&mut self, key: Value) -> VmResult { let mut caller = EnvProtocolCaller; VmResult::Ok(vm_try!(self.table.insert_with(key, (), &mut caller)).is_none()) } @@ -184,9 +213,10 @@ impl HashSet { /// ```rune /// use std::collections::HashSet; /// - /// let set = HashSet::from([1, 2, 3]); - /// assert_eq!(set.contains(1), true); - /// assert_eq!(set.contains(4), false); + /// let set = HashSet::from_iter([1, 2, 3]); + /// + /// assert!(set.contains(1)); + /// assert!(!set.contains(4)); /// ``` #[rune::function(keep)] fn contains(&self, key: Value) -> VmResult { @@ -219,8 +249,8 @@ impl HashSet { /// ```rune /// use std::collections::HashSet; /// - /// let a = HashSet::from([1, 2, 3]); - /// let b = HashSet::from([4, 2, 3, 4]); + /// let a = HashSet::from_iter([1, 2, 3]); + /// let b = HashSet::from_iter([4, 2, 3, 4]); /// /// let diff = a.difference(b).collect::(); /// assert_eq!(diff, [1].iter().collect::()); @@ -231,9 +261,8 @@ impl HashSet { /// assert_eq!(diff, [4].iter().collect::()); /// ``` #[rune::function(keep, instance, path = Self::difference)] - fn difference(this: Ref, other: Ref) -> Iterator { - let iter = Self::difference_inner(this, other); - Iterator::from("std::collections::hash_set::Difference", iter) + fn difference(this: Ref, other: Ref) -> Difference { + Self::difference_inner(this, other) } fn difference_inner(this: Ref, other: Ref) -> Difference { @@ -254,16 +283,16 @@ impl HashSet { /// ```rune /// use std::collections::HashSet; /// - /// let a = HashSet::from([1, 2, 3]); - /// let b = HashSet::from([4, 2, 3, 4]); + /// let a = HashSet::from_iter([1, 2, 3]); + /// let b = HashSet::from_iter([4, 2, 3, 4]); /// /// let values = a.intersection(b).collect::(); /// assert_eq!(values, [2, 3].iter().collect::()); /// ``` #[rune::function(keep, instance, path = Self::intersection)] - fn intersection(this: Ref, other: Ref) -> Iterator { + fn intersection(this: Ref, other: Ref) -> Intersection { // use shortest iterator as driver for intersections - let iter = if this.table.len() <= other.table.len() { + if this.table.len() <= other.table.len() { Intersection { this: Table::iter_ref(Ref::map(this, |this| &this.table)), other: Some(other), @@ -273,9 +302,7 @@ impl HashSet { this: Table::iter_ref(Ref::map(other, |this| &this.table)), other: Some(this), } - }; - - Iterator::from("std::collections::hash_set::Intersection", iter) + } } /// Visits the values representing the union, i.e., all the values in `self` @@ -286,17 +313,17 @@ impl HashSet { /// ```rune /// use std::collections::HashSet; /// - /// let a = HashSet::from([1, 2, 3]); - /// let b = HashSet::from([4, 2, 3, 4]); + /// let a = HashSet::from_iter([1, 2, 3]); + /// let b = HashSet::from_iter([4, 2, 3, 4]); /// /// let union = a.union(b).collect::(); - /// assert_eq!(union, HashSet::from([1, 2, 3, 4])); + /// assert_eq!(union, HashSet::from_iter([1, 2, 3, 4])); /// /// let union = b.union(a).collect::(); - /// assert_eq!(union, HashSet::from([1, 2, 3, 4])); + /// assert_eq!(union, HashSet::from_iter([1, 2, 3, 4])); /// ``` #[rune::function(keep, instance, path = Self::union)] - fn union(this: Ref, other: Ref) -> VmResult { + fn union(this: Ref, other: Ref) -> VmResult { unsafe { let (this, this_guard) = Ref::into_raw(Ref::map(this, |this| &this.table)); let (other, other_guard) = Ref::into_raw(Ref::map(other, |this| &this.table)); @@ -324,7 +351,7 @@ impl HashSet { } }; - VmResult::Ok(Iterator::from("std::collections::hash_set::Union", iter)) + VmResult::Ok(iter) } } @@ -335,19 +362,16 @@ impl HashSet { /// ```rune /// use std::collections::HashSet; /// - /// let set = HashSet::from([3, 2, 1]); + /// let set = HashSet::from_iter([3, 2, 1]); /// let vec = set.iter().collect::(); /// vec.sort(); /// assert_eq!(vec, [1, 2, 3]); /// ``` #[rune::function(keep, instance, path = Self::iter)] - fn iter(this: Ref) -> VmResult { - let iter = Self::iter_inner(this); - VmResult::Ok(Iterator::from("std::collections::hash_set::Iter", iter)) - } + fn iter(this: Ref) -> Iter { + let iter = Table::iter_ref(Ref::map(this, |this| &this.table)); - fn iter_inner(this: Ref) -> impl iter::Iterator { - Table::iter_ref(Ref::map(this, |this| &this.table)).map(|(key, ())| key) + Iter { iter } } /// Extend this set from an iterator. @@ -370,7 +394,7 @@ impl HashSet { /// ```rune /// use std::collections::HashSet; /// - /// let set = HashSet::from([3, 2, 1]); + /// let set = HashSet::from_iter([3, 2, 1]); /// let vec = []; /// /// for value in set { @@ -381,7 +405,7 @@ impl HashSet { /// assert_eq!(vec, [1, 2, 3]); /// ``` #[rune::function(keep, instance, protocol = INTO_ITER, path = Self)] - fn into_iter(this: Ref) -> VmResult { + fn into_iter(this: Ref) -> Iter { Self::iter(this) } @@ -395,7 +419,7 @@ impl HashSet { /// ```rune /// use std::collections::HashSet; /// - /// let set = HashSet::from([1, 2, 3]); + /// let set = HashSet::from_iter([1, 2, 3]); /// println!("{:?}", set); /// ``` #[rune::function(keep, protocol = STRING_DEBUG)] @@ -420,8 +444,33 @@ impl HashSet { VmResult::Ok(()) } - pub(crate) fn from_iter(mut it: Iterator, caller: &mut dyn ProtocolCaller) -> VmResult { - let mut set = vm_try!(Table::try_with_capacity(it.size_hint().0)); + /// Convert a [`HashSet`] from an iterator. + /// + /// The hashmap can be converted from anything that implements the + /// [`INTO_ITER`] protocol, and each item produces should be a tuple pair. + /// + /// # Examples + /// + /// ```rune + /// use std::collections::HashSet; + /// + /// let set = HashSet::from_iter(["a", "b"]); + /// assert_eq!(set.len(), 2); + /// assert!(set.contains("a")); + /// assert!(set.contains("b")); + /// ``` + #[rune::function(keep, path = Self::from_iter)] + fn from_iter(it: Iterator) -> VmResult { + let mut caller = EnvProtocolCaller; + Self::from_iter_with(it, &mut caller) + } + + pub(crate) fn from_iter_with( + mut it: Iterator, + caller: &mut dyn ProtocolCaller, + ) -> VmResult { + let (lo, _) = vm_try!(it.size_hint()); + let mut set = vm_try!(Table::try_with_capacity(lo)); while let Some(key) = vm_try!(it.next()) { vm_try!(set.insert_with(key, (), caller)); @@ -439,9 +488,9 @@ impl HashSet { /// ```rune /// use std::collections::HashSet; /// - /// let set = HashSet::from([1, 2, 3]); - /// assert_eq!(set, HashSet::from([1, 2, 3])); - /// assert_ne!(set, HashSet::from([2, 3, 4])); + /// let set = HashSet::from_iter([1, 2, 3]); + /// assert_eq!(set, HashSet::from_iter([1, 2, 3])); + /// assert_ne!(set, HashSet::from_iter([2, 3, 4])); /// ``` #[rune::function(keep, protocol = PARTIAL_EQ)] fn partial_eq(&self, other: &Self) -> VmResult { @@ -456,9 +505,9 @@ impl HashSet { /// use std::ops::eq; /// use std::collections::HashSet; /// - /// let set = HashSet::from([1, 2, 3]); - /// assert!(eq(set, HashSet::from([1, 2, 3]))); - /// assert!(!eq(set, HashSet::from([2, 3, 4]))); + /// let set = HashSet::from_iter([1, 2, 3]); + /// assert!(eq(set, HashSet::from_iter([1, 2, 3]))); + /// assert!(!eq(set, HashSet::from_iter([2, 3, 4]))); /// ``` #[rune::function(keep, protocol = EQ)] fn eq(&self, other: &Self) -> VmResult { @@ -479,13 +528,7 @@ impl HashSet { VmResult::Ok(true) } - #[rune::function(keep, path = Self::from)] - fn from(value: Value) -> VmResult { - let mut caller = EnvProtocolCaller; - HashSet::from_iter(vm_try!(value.into_iter()), &mut caller) - } - - #[rune::function(keep, instance, path = Self::clone)] + #[rune::function(keep, instance, path = Self::clone, protocol = CLONE)] fn clone(this: &HashSet) -> VmResult { VmResult::Ok(Self { table: vm_try!(this.table.try_clone()), @@ -493,74 +536,102 @@ impl HashSet { } } +#[derive(Any)] +#[rune(item = ::std::collections::hash_set)] +struct Iter { + iter: IterRef<()>, +} + +impl Iter { + #[rune::function(keep, instance, protocol = NEXT)] + pub(crate) fn next(&mut self) -> Option { + let (value, ()) = self.iter.next()?; + Some(value) + } + + #[rune::function(keep, instance, protocol = SIZE_HINT)] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[rune::function(keep, instance, protocol = LEN)] + fn len(&self) -> usize { + self.iter.len() + } +} + +#[derive(Any)] +#[rune(item = ::std::collections::hash_set)] struct Intersection { this: IterRef<()>, other: Option>, } -impl iter::Iterator for Intersection { - type Item = VmResult; - - fn next(&mut self) -> Option { +impl Intersection { + #[rune::function(keep, instance, protocol = NEXT)] + pub(crate) fn next(&mut self) -> VmResult> { let mut caller = EnvProtocolCaller; - let other = self.other.as_ref()?; + + let Some(other) = &self.other else { + return VmResult::Ok(None); + }; for (key, ()) in self.this.by_ref() { - let c = match other.table.get(&key, &mut caller) { - VmResult::Ok(c) => c.is_some(), - VmResult::Err(e) => return Some(VmResult::Err(e)), - }; + let c = vm_try!(other.table.get(&key, &mut caller)).is_some(); if c { - return Some(VmResult::Ok(key)); + return VmResult::Ok(Some(key)); } } self.other = None; - None + VmResult::Ok(None) } - #[inline] + #[rune::function(keep, instance, protocol = SIZE_HINT)] fn size_hint(&self) -> (usize, Option) { let (_, upper) = self.this.size_hint(); (0, upper) } } +#[derive(Any)] +#[rune(item = ::std::collections::hash_set)] struct Difference { this: IterRef<()>, other: Option>, } -impl iter::Iterator for Difference { - type Item = VmResult; - - fn next(&mut self) -> Option { +impl Difference { + #[rune::function(keep, instance, protocol = NEXT)] + pub(crate) fn next(&mut self) -> VmResult> { let mut caller = EnvProtocolCaller; - let other = self.other.as_ref()?; + + let Some(other) = &self.other else { + return VmResult::Ok(None); + }; for (key, ()) in self.this.by_ref() { - let c = match other.table.get(&key, &mut caller) { - VmResult::Ok(c) => c.is_some(), - VmResult::Err(e) => return Some(VmResult::Err(e)), - }; + let c = vm_try!(other.table.get(&key, &mut caller)).is_some(); if !c { - return Some(VmResult::Ok(key)); + return VmResult::Ok(Some(key)); } } self.other = None; - None + VmResult::Ok(None) } - #[inline] + #[rune::function(keep, instance, protocol = SIZE_HINT)] fn size_hint(&self) -> (usize, Option) { let (_, upper) = self.this.size_hint(); (0, upper) } } +#[derive(Any)] +#[rune(item = ::std::collections::hash_set)] struct Union { this: ptr::NonNull>, this_iter: RawIter<(Value, ())>, @@ -568,16 +639,15 @@ struct Union { _guards: (RawRef, RawRef), } -impl iter::Iterator for Union { - type Item = VmResult; - - fn next(&mut self) -> Option { +impl Union { + #[rune::function(keep, instance, protocol = NEXT)] + fn next(&mut self) -> VmResult> { // SAFETY: we're holding onto the ref guards for both collections during // iteration, so this is valid for the lifetime of the iterator. unsafe { if let Some(bucket) = self.this_iter.next() { let (value, ()) = bucket.as_ref(); - return Some(VmResult::Ok(value.clone())); + return VmResult::Ok(Some(value.clone())); } let mut caller = EnvProtocolCaller; @@ -585,14 +655,12 @@ impl iter::Iterator for Union { for bucket in self.other_iter.by_ref() { let (key, ()) = bucket.as_ref(); - match self.this.as_ref().get(key, &mut caller) { - VmResult::Ok(None) => return Some(VmResult::Ok(key.clone())), - VmResult::Ok(..) => {} - VmResult::Err(e) => return Some(VmResult::Err(e)), + if vm_try!(self.this.as_ref().get(key, &mut caller)).is_none() { + return VmResult::Ok(Some(key.clone())); } } - None + VmResult::Ok(None) } } } diff --git a/crates/rune/src/modules/collections/mod.rs b/crates/rune/src/modules/collections/mod.rs new file mode 100644 index 000000000..d13e1e241 --- /dev/null +++ b/crates/rune/src/modules/collections/mod.rs @@ -0,0 +1,42 @@ +//! Dynamic collections. + +#[cfg(feature = "alloc")] +pub(crate) mod hash_map; +#[cfg(feature = "alloc")] +pub(crate) use hash_map::HashMap; + +#[cfg(feature = "alloc")] +pub(crate) mod hash_set; +#[cfg(feature = "alloc")] +pub(crate) use hash_set::HashSet; + +#[cfg(feature = "alloc")] +pub(crate) mod vec_deque; +#[cfg(feature = "alloc")] +pub(crate) use vec_deque::VecDeque; + +use crate as rune; +use crate::{ContextError, Module}; + +/// Module defining collections. +#[rune::module(::std::collections)] +pub fn module() -> Result { + let mut m = Module::from_meta(self::module_meta)?; + + m.reexport( + ["HashMap"], + rune::item!(::std::collections::hash_map::HashMap), + )?; + + m.reexport( + ["HashSet"], + rune::item!(::std::collections::hash_set::HashSet), + )?; + + m.reexport( + ["VecDeque"], + rune::item!(::std::collections::vec_deque::VecDeque), + )?; + + Ok(m) +} diff --git a/crates/rune/src/modules/collections/vec_deque.rs b/crates/rune/src/modules/collections/vec_deque.rs index 776f4ad7e..5c20bfbab 100644 --- a/crates/rune/src/modules/collections/vec_deque.rs +++ b/crates/rune/src/modules/collections/vec_deque.rs @@ -6,47 +6,54 @@ use crate::alloc; use crate::alloc::fmt::TryWrite; use crate::alloc::prelude::*; use crate::runtime::{ - EnvProtocolCaller, Formatter, Iterator, Protocol, ProtocolCaller, RawRef, Ref, Value, + EnvProtocolCaller, Formatter, Iterator, Protocol, ProtocolCaller, RawRef, Ref, Value, Vec, VmErrorKind, VmResult, }; use crate::{Any, ContextError, Module}; -pub(super) fn setup(m: &mut Module) -> Result<(), ContextError> { - m.ty::()?.docs([ - "A double-ended queue implemented with a growable ring buffer.", - "", - "The \"default\" usage of this type as a queue is to use [`push_back`] to add to", - "the queue, and [`pop_front`] to remove from the queue. [`extend`] and [`append`]", - "push onto the back in this manner, and iterating over `VecDeque` goes front", - "to back.", - "", - "A `VecDeque` with a known list of items can be initialized from an array:", - "", - "```rune", - "use std::collections::VecDeque;", - "", - "let deq = VecDeque::from([-1, 0, 1]);", - "```", - "", - "[`push_back`]: VecDeque::push_back", - "[`pop_front`]: VecDeque::pop_front", - "[`extend`]: VecDeque::extend", - "[`append`]: VecDeque::append", - ])?; +/// A dynamic vec deque. +#[rune::module(::std::collections::vec_deque)] +pub fn module() -> Result { + let mut m = Module::from_meta(self::module_meta)?; + + m.ty::()?.docs(docstring! { + /// A double-ended queue implemented with a growable ring buffer. + /// + /// The "default" usage of this type as a queue is to use [`push_back`] + /// to add to the queue, and [`pop_front`] to remove from the queue. + /// [`extend`] and [`append`] push onto the back in this manner, and + /// iterating over `VecDeque` goes front to back. + /// + /// A `VecDeque` with a known list of items can be initialized from an + /// array: + /// + /// ```rune + /// use std::collections::VecDeque; + /// + /// let deq = VecDeque::from::([-1, 0, 1]); + /// ``` + /// + /// [`push_back`]: VecDeque::push_back + /// [`pop_front`]: VecDeque::pop_front + /// [`extend`]: VecDeque::extend + /// [`append`]: VecDeque::append + })?; m.function_meta(VecDeque::new)?; - m.function_meta(VecDeque::with_capacity)?; + m.function_meta(VecDeque::with_capacity__meta)?; m.function_meta(from)?; m.function_meta(VecDeque::extend)?; m.function_meta(VecDeque::insert)?; - m.function_meta(VecDeque::iter)?; + m.function_meta(VecDeque::iter__meta)?; + m.function_meta(VecDeque::into_iter__meta)?; + m.function_meta(VecDeque::from_iter__meta)?; m.function_meta(VecDeque::reserve)?; m.function_meta(VecDeque::len)?; m.function_meta(VecDeque::capacity)?; m.function_meta(VecDeque::front)?; m.function_meta(VecDeque::back)?; - m.function_meta(VecDeque::push_back)?; + m.function_meta(VecDeque::push_back__meta)?; m.function_meta(VecDeque::push_front)?; m.function_meta(VecDeque::pop_front)?; m.function_meta(VecDeque::pop_back)?; @@ -56,17 +63,37 @@ pub(super) fn setup(m: &mut Module) -> Result<(), ContextError> { m.associated_function(Protocol::INDEX_GET, VecDeque::get)?; m.associated_function(Protocol::INDEX_SET, VecDeque::set)?; - m.associated_function(Protocol::INTO_ITER, VecDeque::__rune_fn__iter)?; + m.function_meta(VecDeque::string_debug)?; - m.function_meta(VecDeque::partial_eq)?; - m.function_meta(VecDeque::eq)?; - m.function_meta(VecDeque::partial_cmp)?; - m.function_meta(VecDeque::cmp)?; - Ok(()) + + m.function_meta(VecDeque::partial_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + + m.function_meta(VecDeque::eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + + m.function_meta(VecDeque::partial_cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialOrd))?; + + m.function_meta(VecDeque::cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Ord))?; + + m.ty::()?; + m.function_meta(Iter::next__meta)?; + m.function_meta(Iter::size_hint__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + + m.function_meta(Iter::next_back__meta)?; + m.implement_trait::(rune::item!(::std::iter::DoubleEndedIterator))?; + + m.function_meta(Iter::len__meta)?; + m.implement_trait::(rune::item!(::std::iter::ExactSizeIterator))?; + + Ok(m) } #[derive(Any, Default)] -#[rune(module = crate, item = ::std::collections)] +#[rune(module = crate, item = ::std::collections::vec_deque)] pub(crate) struct VecDeque { inner: alloc::VecDeque, } @@ -98,8 +125,8 @@ impl VecDeque { /// let deque = VecDeque::with_capacity(10); /// assert!(deque.capacity() >= 10); /// ``` - #[rune::function(path = Self::with_capacity)] - fn with_capacity(count: usize) -> VmResult { + #[rune::function(keep, path = Self::with_capacity)] + pub(crate) fn with_capacity(count: usize) -> VmResult { VmResult::Ok(Self { inner: vm_try!(alloc::VecDeque::try_with_capacity(count)), }) @@ -182,8 +209,8 @@ impl VecDeque { /// buf.push_back(3); /// assert_eq!(Some(3), buf.back()); /// ``` - #[rune::function] - fn push_back(&mut self, value: Value) -> VmResult<()> { + #[rune::function(keep)] + pub(crate) fn push_back(&mut self, value: Value) -> VmResult<()> { vm_try!(self.inner.try_push_back(value)); VmResult::Ok(()) } @@ -259,7 +286,7 @@ impl VecDeque { /// ```rune /// use std::collections::VecDeque; /// - /// let buf = VecDeque::from([1]); + /// let buf = VecDeque::from::([1]); /// buf.reserve(10); /// assert!(buf.capacity() >= 11); /// ``` @@ -282,7 +309,7 @@ impl VecDeque { /// assert_eq!(deque.len(), 1); /// ``` #[rune::function] - fn len(&mut self) -> usize { + fn len(&self) -> usize { self.inner.len() } @@ -471,40 +498,39 @@ impl VecDeque { /// assert_eq!([4, 3, 5], buf.iter().rev()); /// ``` #[inline] - #[rune::function(instance, path = Self::iter)] - fn iter(this: Ref) -> Iterator { - struct Iter { - iter: alloc::vec_deque::RawIter, - // Drop must happen after the raw iterator. - _guard: RawRef, - } - - impl iter::Iterator for Iter { - type Item = Value; - - #[inline] - fn next(&mut self) -> Option { - // SAFETY: We're holding onto the reference guard. - unsafe { Some((*self.iter.next()?).clone()) } - } - } - - impl iter::DoubleEndedIterator for Iter { - fn next_back(&mut self) -> Option { - // SAFETY: We're holding onto the reference guard. - unsafe { Some((*self.iter.next_back()?).clone()) } - } - } - + #[rune::function(keep, instance, path = Self::iter)] + fn iter(this: Ref) -> Iter { // SAFETY: We're holding onto the reference guard. let iter = unsafe { this.inner.raw_iter() }; let (_, _guard) = Ref::into_raw(this); - let iter = Iter { iter, _guard }; - Iterator::from_double_ended("std::collections::vec_deque::Iter", iter) + Iter { iter, _guard } } - pub(crate) fn from_iter(mut it: Iterator) -> VmResult { - let mut inner = vm_try!(alloc::VecDeque::try_with_capacity(it.size_hint().0,)); + #[rune::function(keep, instance, protocol = INTO_ITER, path = Self)] + fn into_iter(this: Ref) -> Iter { + Self::iter(this) + } + + /// Build a [`VecDeque`] from an iterator. + /// + /// The hashmap can be converted from anything that implements the + /// [`INTO_ITER`] protocol, and each item produces should be a tuple pair. + /// + /// # Examples + /// + /// ```rune + /// use std::collections::VecDeque; + /// + /// let deque = VecDeque::from_iter(["a", "b", "c"]); + /// assert_eq!(deque.len(), 3); + /// assert_eq!(deque.pop_front(), Some("a")); + /// assert_eq!(deque.pop_back(), Some("c")); + /// assert_eq!(deque.len(), 1); + /// ``` + #[rune::function(keep, path = Self::from_iter)] + fn from_iter(mut it: Iterator) -> VmResult { + let (lo, _) = vm_try!(it.size_hint()); + let mut inner = vm_try!(alloc::VecDeque::try_with_capacity(lo)); while let Some(value) = vm_try!(it.next()) { vm_try!(inner.try_push_back(value)); @@ -546,7 +572,7 @@ impl VecDeque { /// ```rune /// use std::collections::VecDeque; /// - /// let deque = VecDeque::from([1, 2, 3]); + /// let deque = VecDeque::from::([1, 2, 3]); /// assert_eq!(format!("{:?}", deque), "[1, 2, 3]"); /// ``` #[rune::function(protocol = STRING_DEBUG)] @@ -586,13 +612,13 @@ impl VecDeque { /// ```rune /// use std::collections::VecDeque; /// - /// let deque = VecDeque::from([1, 2, 3]); + /// let deque = VecDeque::from::([1, 2, 3]); /// /// assert!(deque == [1, 2, 3]); /// assert!(deque == (1..=3)); /// assert!(deque != [2, 3, 4]); /// ``` - #[rune::function(protocol = PARTIAL_EQ)] + #[rune::function(keep, protocol = PARTIAL_EQ)] fn partial_eq(&self, b: Value) -> VmResult { self.partial_eq_with(b, &mut EnvProtocolCaller) } @@ -625,12 +651,12 @@ impl VecDeque { /// use std::collections::VecDeque; /// use std::ops::eq; /// - /// let deque = VecDeque::from([1, 2, 3]); + /// let deque = VecDeque::from::([1, 2, 3]); /// - /// assert!(eq(deque, VecDeque::from([1, 2, 3]))); - /// assert!(!eq(deque, VecDeque::from([2, 3, 4]))); + /// assert!(eq(deque, VecDeque::from::([1, 2, 3]))); + /// assert!(!eq(deque, VecDeque::from::([2, 3, 4]))); /// ``` - #[rune::function(protocol = EQ)] + #[rune::function(keep, protocol = EQ)] fn eq(&self, b: &VecDeque) -> VmResult { self.eq_with(b, &mut EnvProtocolCaller) } @@ -662,12 +688,12 @@ impl VecDeque { /// ```rune /// use std::collections::VecDeque; /// - /// let deque = VecDeque::from([1, 2, 3]); + /// let deque = VecDeque::from::([1, 2, 3]); /// - /// assert!(deque > VecDeque::from([0, 2, 3])); - /// assert!(deque < VecDeque::from([2, 2, 3])); + /// assert!(deque > VecDeque::from::([0, 2, 3])); + /// assert!(deque < VecDeque::from::([2, 2, 3])); /// ``` - #[rune::function(protocol = PARTIAL_CMP)] + #[rune::function(keep, protocol = PARTIAL_CMP)] fn partial_cmp(&self, b: &VecDeque) -> VmResult> { self.partial_cmp_with(b, &mut EnvProtocolCaller) } @@ -706,12 +732,12 @@ impl VecDeque { /// use std::cmp::Ordering; /// use std::ops::cmp; /// - /// let deque = VecDeque::from([1, 2, 3]); + /// let deque = VecDeque::from::([1, 2, 3]); /// - /// assert_eq!(cmp(deque, VecDeque::from([0, 2, 3])), Ordering::Greater); - /// assert_eq!(cmp(deque, VecDeque::from([2, 2, 3])), Ordering::Less); + /// assert_eq!(cmp(deque, VecDeque::from::([0, 2, 3])), Ordering::Greater); + /// assert_eq!(cmp(deque, VecDeque::from::([2, 2, 3])), Ordering::Less); /// ``` - #[rune::function(protocol = CMP)] + #[rune::function(keep, protocol = CMP)] fn cmp(&self, b: &VecDeque) -> VmResult { self.cmp_with(b, &mut EnvProtocolCaller) } @@ -738,6 +764,14 @@ impl VecDeque { } } +impl From for VecDeque { + fn from(value: Vec) -> Self { + Self { + inner: alloc::VecDeque::from(value.into_inner()), + } + } +} + impl TryClone for VecDeque { #[inline] fn try_clone(&self) -> alloc::Result { @@ -752,16 +786,65 @@ impl TryClone for VecDeque { } } -/// Construct a [`VecDeque`] from a value. +/// Construct a [`VecDeque`] from a [`Vec`]. +/// +/// This is a cheap conversion. /// /// # Examples /// /// ```rune /// use std::collections::VecDeque; /// -/// let buf = VecDeque::from([1, 2, 3]); +/// let buf = VecDeque::from::([1, 2, 3]); /// ``` -#[rune::function(free, path = VecDeque::from)] -fn from(value: Value) -> VmResult { - VecDeque::from_iter(vm_try!(value.into_iter())) +#[rune::function(free, path = VecDeque::from::)] +fn from(vec: Vec) -> VmResult { + VmResult::Ok(VecDeque::from(vec)) +} + +#[derive(Any)] +#[rune(item = ::std::collections::vec_deque)] +pub(crate) struct Iter { + iter: alloc::vec_deque::RawIter, + // Drop must happen after the raw iterator. + _guard: RawRef, +} + +impl Iter { + #[rune::function(keep, protocol = NEXT)] + fn next(&mut self) -> Option { + // SAFETY: We're holding onto the reference guard. + unsafe { Some((*self.iter.next()?).clone()) } + } + + #[rune::function(keep, protocol = SIZE_HINT)] + fn size_hint(self) -> (usize, Option) { + self.iter.size_hint() + } + + #[rune::function(keep, protocol = LEN)] + fn len(self) -> usize { + self.iter.len() + } + + #[rune::function(keep, protocol = NEXT_BACK)] + fn next_back(&mut self) -> Option { + // SAFETY: We're holding onto the reference guard. + unsafe { Some((*self.iter.next_back()?).clone()) } + } +} + +impl iter::Iterator for Iter { + type Item = Value; + + #[inline] + fn next(&mut self) -> Option { + Iter::next(self) + } +} + +impl iter::DoubleEndedIterator for Iter { + fn next_back(&mut self) -> Option { + Iter::next_back(self) + } } diff --git a/crates/rune/src/modules/core.rs b/crates/rune/src/modules/core.rs index f61449007..ecc22e7f7 100644 --- a/crates/rune/src/modules/core.rs +++ b/crates/rune/src/modules/core.rs @@ -86,25 +86,6 @@ fn is_writable(value: Value) -> bool { value.is_writable() } -/// Clone the specified `value`. -/// -/// # Examples -/// -/// ```rune -/// let a = 42; -/// let b = a; -/// let c = clone(a); -/// -/// a += 1; -/// assert_eq!(a, 43); -/// assert_eq!(b, 43); -/// assert_eq!(c, 42); -/// ``` -#[rune::function] -fn clone(value: Value) -> Value { - value.clone() -} - /// Stringify the given argument, causing it to expand to its underlying token /// stream. /// diff --git a/crates/rune/src/modules/disable_io.rs b/crates/rune/src/modules/disable_io.rs index 00ff47033..16a37b742 100644 --- a/crates/rune/src/modules/disable_io.rs +++ b/crates/rune/src/modules/disable_io.rs @@ -12,7 +12,7 @@ //! ``` use crate as rune; -use crate::runtime::{InstAddress, Output, Stack, VmResult}; +use crate::runtime::{InstAddress, Memory, Output, VmResult}; use crate::{ContextError, Module}; /// I/O methods which will cause any output to be ignored. @@ -26,7 +26,7 @@ pub fn module() -> Result { module .raw_function( "dbg", - move |stack: &mut Stack, _: InstAddress, _: usize, out: Output| { + move |stack: &mut dyn Memory, _: InstAddress, _: usize, out: Output| { vm_try!(out.store(stack, ())); VmResult::Ok(()) }, diff --git a/crates/rune/src/modules/f64.rs b/crates/rune/src/modules/f64.rs index 5ec6c8125..5c7aa73bc 100644 --- a/crates/rune/src/modules/f64.rs +++ b/crates/rune/src/modules/f64.rs @@ -39,10 +39,21 @@ pub fn module() -> Result { #[cfg(feature = "std")] m.function_meta(round)?; m.function_meta(to_integer)?; - m.function_meta(partial_eq)?; - m.function_meta(eq)?; - m.function_meta(partial_cmp)?; - m.function_meta(cmp)?; + + m.function_meta(clone__meta)?; + m.implement_trait::(rune::item!(::std::clone::Clone))?; + + m.function_meta(partial_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + + m.function_meta(eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + + m.function_meta(partial_cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialOrd))?; + + m.function_meta(cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Ord))?; m.constant("EPSILON", f64::EPSILON).build()?; m.constant("MIN", f64::MIN).build()?; @@ -369,6 +380,27 @@ fn round(this: f64) -> f64 { this.round() } +/// Test two integers for partial equality. +/// +/// # Examples +/// +/// ```rune +/// let a = 5.0; +/// let b = a; +/// let c = a.clone(); +/// +/// a += 1.0; +/// +/// assert_eq!(a, 6.0); +/// assert_eq!(b, 6.0); +/// assert_eq!(c, 5.0); +/// ``` +#[rune::function(keep, instance, protocol = CLONE)] +#[inline] +fn clone(this: f64) -> f64 { + this +} + /// Test two floats for partial equality. /// /// # Examples @@ -380,7 +412,7 @@ fn round(this: f64) -> f64 { /// assert!(10.0 != f64::NAN); /// assert!(f64::NAN != f64::NAN); /// ``` -#[rune::function(instance, protocol = PARTIAL_EQ)] +#[rune::function(keep, instance, protocol = PARTIAL_EQ)] #[inline] fn partial_eq(this: f64, rhs: f64) -> bool { this.eq(&rhs) @@ -397,7 +429,7 @@ fn partial_eq(this: f64, rhs: f64) -> bool { /// assert_eq!(eq(5.0, 10.0), false); /// assert_eq!(eq(10.0, 5.0), false); /// ``` -#[rune::function(instance, protocol = EQ)] +#[rune::function(keep, instance, protocol = EQ)] #[inline] fn eq(this: f64, rhs: f64) -> VmResult { let Some(ordering) = this.partial_cmp(&rhs) else { @@ -420,7 +452,7 @@ fn eq(this: f64, rhs: f64) -> VmResult { /// assert_eq!(partial_cmp(5.0, 5.0), Some(Ordering::Equal)); /// assert_eq!(partial_cmp(5.0, f64::NAN), None); /// ``` -#[rune::function(instance, protocol = PARTIAL_CMP)] +#[rune::function(keep, instance, protocol = PARTIAL_CMP)] #[inline] fn partial_cmp(this: f64, rhs: f64) -> Option { this.partial_cmp(&rhs) @@ -438,7 +470,7 @@ fn partial_cmp(this: f64, rhs: f64) -> Option { /// assert_eq!(cmp(10.0, 5.0), Ordering::Greater); /// assert_eq!(cmp(5.0, 5.0), Ordering::Equal); /// ``` -#[rune::function(instance, protocol = CMP)] +#[rune::function(keep, instance, protocol = CMP)] #[inline] fn cmp(this: f64, rhs: f64) -> VmResult { let Some(ordering) = this.partial_cmp(&rhs) else { diff --git a/crates/rune/src/modules/i64.rs b/crates/rune/src/modules/i64.rs index 5c4fb7e74..ff63a088a 100644 --- a/crates/rune/src/modules/i64.rs +++ b/crates/rune/src/modules/i64.rs @@ -14,71 +14,81 @@ use crate::{ContextError, Module}; /// This provides methods for computing over and parsing 64-bit integers. #[rune::module(::std::i64)] pub fn module() -> Result { - let mut module = Module::from_meta(self::module_meta)?; - - module.function("parse", parse).build()?; - module.function_meta(to_float)?; - - module.function_meta(max)?; - module.function_meta(min)?; - module.function_meta(abs)?; - module.function_meta(pow)?; - - module.function_meta(checked_add)?; - module.function_meta(checked_sub)?; - module.function_meta(checked_div)?; - module.function_meta(checked_mul)?; - module.function_meta(checked_rem)?; - - module.function_meta(wrapping_add)?; - module.function_meta(wrapping_sub)?; - module.function_meta(wrapping_div)?; - module.function_meta(wrapping_mul)?; - module.function_meta(wrapping_rem)?; - - module.function_meta(saturating_add)?; - module.function_meta(saturating_sub)?; - module.function_meta(saturating_mul)?; - module.function_meta(saturating_abs)?; - module.function_meta(saturating_pow)?; - - module.function_meta(signum)?; - module.function_meta(is_positive)?; - module.function_meta(is_negative)?; - - module.function_meta(partial_eq)?; - module.function_meta(eq)?; - module.function_meta(partial_cmp)?; - module.function_meta(cmp)?; - module.function_meta(to_string)?; - - module.constant("MIN", i64::MIN).build()?.docs([ - "The smallest value that can be represented by this integer type", - "(−263).", - "", - "# Examples", - "", - "Basic usage:", - "", - "```rune", - "assert_eq!(i64::MIN, -9223372036854775808);", - "```", - ])?; - - module.constant("MAX", i64::MAX).build()?.docs([ - "The largest value that can be represented by this integer type", - "(263 − 1).", - "", - "# Examples", - "", - "Basic usage:", - "", - "```rune", - "assert_eq!(i64::MAX, 9223372036854775807);", - "```", - ])?; - - Ok(module) + let mut m = Module::from_meta(self::module_meta)?; + + m.function("parse", parse).build()?; + m.function_meta(to_float)?; + + m.function_meta(max)?; + m.function_meta(min)?; + m.function_meta(abs)?; + m.function_meta(pow)?; + + m.function_meta(checked_add)?; + m.function_meta(checked_sub)?; + m.function_meta(checked_div)?; + m.function_meta(checked_mul)?; + m.function_meta(checked_rem)?; + + m.function_meta(wrapping_add)?; + m.function_meta(wrapping_sub)?; + m.function_meta(wrapping_div)?; + m.function_meta(wrapping_mul)?; + m.function_meta(wrapping_rem)?; + + m.function_meta(saturating_add)?; + m.function_meta(saturating_sub)?; + m.function_meta(saturating_mul)?; + m.function_meta(saturating_abs)?; + m.function_meta(saturating_pow)?; + + m.function_meta(signum)?; + m.function_meta(is_positive)?; + m.function_meta(is_negative)?; + m.function_meta(to_string)?; + + m.function_meta(clone__meta)?; + m.implement_trait::(rune::item!(::std::clone::Clone))?; + + m.function_meta(partial_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + + m.function_meta(eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + + m.function_meta(partial_cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialOrd))?; + + m.function_meta(cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Ord))?; + + m.constant("MIN", i64::MIN).build()?.docs(docstring! { + /// The smallest value that can be represented by this integer type + /// (−263). + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert_eq!(i64::MIN, -9223372036854775808); + /// ``` + })?; + + m.constant("MAX", i64::MAX).build()?.docs(docstring! { + /// The largest value that can be represented by this integer type + /// (263 − 1). + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// assert_eq!(i64::MAX, 9223372036854775807); + /// ``` + })?; + + Ok(m) } /// Parse an `int`. @@ -520,6 +530,27 @@ fn is_negative(this: i64) -> bool { i64::is_negative(this) } +/// Test two integers for partial equality. +/// +/// # Examples +/// +/// ```rune +/// let a = 5; +/// let b = a; +/// let c = a.clone(); +/// +/// a += 1; +/// +/// assert_eq!(a, 6); +/// assert_eq!(b, 6); +/// assert_eq!(c, 5); +/// ``` +#[rune::function(keep, instance, protocol = CLONE)] +#[inline] +fn clone(this: i64) -> i64 { + this +} + /// Test two integers for partial equality. /// /// # Examples @@ -531,7 +562,7 @@ fn is_negative(this: i64) -> bool { /// assert_eq!(partial_eq(5, 10), false); /// assert_eq!(partial_eq(10, 5), false); /// ``` -#[rune::function(instance, protocol = PARTIAL_EQ)] +#[rune::function(keep, instance, protocol = PARTIAL_EQ)] #[inline] fn partial_eq(this: i64, rhs: i64) -> bool { this.eq(&rhs) @@ -548,7 +579,7 @@ fn partial_eq(this: i64, rhs: i64) -> bool { /// assert_eq!(eq(5, 10), false); /// assert_eq!(eq(10, 5), false); /// ``` -#[rune::function(instance, protocol = EQ)] +#[rune::function(keep, instance, protocol = EQ)] #[inline] fn eq(this: i64, rhs: i64) -> bool { this.eq(&rhs) @@ -566,7 +597,7 @@ fn eq(this: i64, rhs: i64) -> bool { /// assert_eq!(partial_cmp(10, 5), Some(Ordering::Greater)); /// assert_eq!(partial_cmp(5, 5), Some(Ordering::Equal)); /// ``` -#[rune::function(instance, protocol = PARTIAL_CMP)] +#[rune::function(keep, instance, protocol = PARTIAL_CMP)] #[inline] fn partial_cmp(this: i64, rhs: i64) -> Option { this.partial_cmp(&rhs) @@ -584,7 +615,7 @@ fn partial_cmp(this: i64, rhs: i64) -> Option { /// assert_eq!(cmp(10, 5), Ordering::Greater); /// assert_eq!(cmp(5, 5), Ordering::Equal); /// ``` -#[rune::function(instance, protocol = CMP)] +#[rune::function(keep, instance, protocol = CMP)] #[inline] fn cmp(this: i64, rhs: i64) -> Ordering { this.cmp(&rhs) diff --git a/crates/rune/src/modules/io.rs b/crates/rune/src/modules/io.rs index e0e37910a..29904b4dc 100644 --- a/crates/rune/src/modules/io.rs +++ b/crates/rune/src/modules/io.rs @@ -10,7 +10,7 @@ use crate::compile; use crate::macros::{quote, FormatArgs, MacroContext, TokenStream}; use crate::parse::Parser; #[cfg(feature = "std")] -use crate::runtime::{Formatter, InstAddress, Output, Panic, Stack, VmResult}; +use crate::runtime::{Formatter, InstAddress, Memory, Output, Panic, VmResult}; use crate::{ContextError, Module}; /// I/O functions. @@ -20,21 +20,21 @@ pub fn module( ) -> Result { let mut module = Module::from_meta(self::module_meta)?.with_unique("std::io"); - module.item_mut().docs([ - "The std::io module contains a number of common things", - "you’ll need when doing input and output.", - "The most core parts of this module are the [print()], [println()], ", - "and [dbg()] functions which are used to hook up printing for a Rune project.", - "", - "With complete names:", - "* `::std::io::print`", - "* `::std::io::println`", - "* `::std::io::dbg`", - "", - "Their definitions can be omitted from the built-in standard library, and", - "can then easily be defined by third party modules allowing for printing", - "to be hooked up to whatever system you want.", - ])?; + module.item_mut().docs(docstring! { + /// The std::io module contains a number of common things + /// you’ll need when doing input and output. + /// The most core parts of this module are the [print()], [println()], + /// and [dbg()] functions which are used to hook up printing for a Rune project. + /// + /// With complete names: + /// * `::std::io::print` + /// * `::std::io::println` + /// * `::std::io::dbg` + /// + /// Their definitions can be omitted from the built-in standard library, and + /// can then easily be defined by third party modules allowing for printing + /// to be hooked up to whatever system you want. + })?; #[cfg(feature = "std")] module.ty::()?; @@ -46,25 +46,28 @@ pub fn module( module.function_meta(print_impl)?; module.function_meta(println_impl)?; - module.raw_function("dbg", dbg_impl).build()?.docs([ - "Debug to output.", - "", - "This is the actual output hook, and if you install rune modules without", - "`I/O` enabled this will not be defined. It is then up to someone else to", - "provide an implementation.", - "", - "# Examples", - "", - "```rune", - "let number = 10;", - "let number = number * 4;", - "", - "let who = \"World\";", - "let string = format!(\"Hello {}\", who);", - "", - "dbg(number, string);", - "```", - ])?; + module + .raw_function("dbg", dbg_impl) + .build()? + .docs(docstring! { + /// Debug to output. + /// + /// This is the actual output hook, and if you install rune modules without + /// `I/O` enabled this will not be defined. It is then up to someone else to + /// provide an implementation. + /// + /// # Examples + /// + /// ```rune + /// let number = 10; + /// let number = number * 4; + /// + /// let who = "World"; + /// let string = format!("Hello {who}"); + /// + /// dbg(number, string); + /// ``` + })?; } // These are unconditionally included, but using them might cause a @@ -83,7 +86,7 @@ fn io_error_string_display(error: &io::Error, f: &mut Formatter) -> VmResult<()> } #[cfg(feature = "std")] -fn dbg_impl(stack: &mut Stack, addr: InstAddress, args: usize, out: Output) -> VmResult<()> { +fn dbg_impl(stack: &mut dyn Memory, addr: InstAddress, args: usize, out: Output) -> VmResult<()> { let stdout = io::stdout(); let mut stdout = stdout.lock(); diff --git a/crates/rune/src/modules/iter.rs b/crates/rune/src/modules/iter.rs index 122a8f31c..fc93ae84b 100644 --- a/crates/rune/src/modules/iter.rs +++ b/crates/rune/src/modules/iter.rs @@ -1,69 +1,1749 @@ //! Iterators. -use core::convert::identity; - use crate as rune; -use crate::alloc::String; +use crate::alloc; +use crate::alloc::prelude::*; use crate::modules::collections::VecDeque; #[cfg(feature = "alloc")] use crate::modules::collections::{HashMap, HashSet}; -#[cfg(feature = "alloc")] -use crate::runtime::EnvProtocolCaller; +use crate::runtime::range::RangeIter; use crate::runtime::{ - FromValue, Function, Iterator, Object, OwnedTuple, Protocol, Value, ValueKind, Vec, VmResult, + CoreTypeOf, FromValue, Function, InstAddress, Object, Output, OwnedTuple, Protocol, Value, + ValueKind, Vec, VmErrorKind, VmResult, }; -use crate::{ContextError, Module}; +use crate::shared::Caller; +use crate::{Any, ContextError, Module, Params}; -/// Iterators. +/// Rune support for iterators. /// /// This module contains types and methods for working with iterators in Rune. #[rune::module(::std::iter)] pub fn module() -> Result { - let mut module = Module::from_meta(self::module_meta)?; - - module.ty::()?; - - module.function_meta(next)?; - module.function_meta(next_back)?; - module.function_meta(find)?; - module.function_meta(any)?; - module.function_meta(all)?; - module.function_meta(chain)?; - module.function_meta(filter)?; - module.function_meta(filter_map)?; - module.function_meta(map)?; - module.function_meta(flat_map)?; - module.function_meta(enumerate)?; - module.function_meta(peek)?; - module.function_meta(peekable)?; - module.function_meta(sum_int)?; - module.function_meta(sum_byte)?; - module.function_meta(sum_float)?; - module.function_meta(product_int)?; - module.function_meta(product_byte)?; - module.function_meta(product_float)?; - module.function_meta(fold)?; - module.function_meta(reduce)?; - module.function_meta(rev)?; - module.function_meta(size_hint)?; - module.function_meta(skip)?; - module.function_meta(take)?; - module.function_meta(count)?; - module.associated_function(Protocol::NEXT, Iterator::next)?; - module.associated_function(Protocol::INTO_ITER, identity::)?; - - module.function_meta(range)?; - module.function_meta(empty)?; - module.function_meta(once)?; - - module.function_meta(collect_vec)?; - module.function_meta(collect_vec_deque)?; - module.function_meta(collect_hash_set)?; - module.function_meta(collect_hash_map)?; - module.function_meta(collect_tuple)?; - module.function_meta(collect_object)?; - module.function_meta(collect_string)?; - Ok(module) + let mut m = Module::from_meta(self::module_meta)?; + + m.ty::()?; + m.function_meta(Rev::next__meta)?; + m.function_meta(Rev::next_back__meta)?; + m.function_meta(Rev::size_hint__meta)?; + m.function_meta(Rev::len__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.implement_trait::(rune::item!(::std::iter::DoubleEndedIterator))?; + m.implement_trait::(rune::item!(::std::iter::ExactSizeIterator))?; + + m.ty::()?; + m.function_meta(Chain::next__meta)?; + m.function_meta(Chain::next_back__meta)?; + m.function_meta(Chain::size_hint__meta)?; + m.function_meta(Chain::len__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.implement_trait::(rune::item!(::std::iter::DoubleEndedIterator))?; + m.implement_trait::(rune::item!(::std::iter::ExactSizeIterator))?; + + m.ty::()?; + m.function_meta(Enumerate::next__meta)?; + m.function_meta(Enumerate::next_back__meta)?; + m.function_meta(Enumerate::size_hint__meta)?; + m.function_meta(Enumerate::len__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.implement_trait::(rune::item!(::std::iter::DoubleEndedIterator))?; + m.implement_trait::(rune::item!(::std::iter::ExactSizeIterator))?; + + m.ty::()?; + m.function_meta(Filter::next__meta)?; + m.function_meta(Filter::next_back__meta)?; + m.function_meta(Filter::size_hint__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.implement_trait::(rune::item!(::std::iter::DoubleEndedIterator))?; + + m.ty::()?; + m.function_meta(Map::next__meta)?; + m.function_meta(Map::next_back__meta)?; + m.function_meta(Map::size_hint__meta)?; + m.function_meta(Map::len__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.implement_trait::(rune::item!(::std::iter::DoubleEndedIterator))?; + m.implement_trait::(rune::item!(::std::iter::ExactSizeIterator))?; + + m.ty::()?; + m.function_meta(FilterMap::next__meta)?; + m.function_meta(FilterMap::next_back__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.implement_trait::(rune::item!(::std::iter::DoubleEndedIterator))?; + + m.ty::()?; + m.function_meta(FlatMap::next__meta)?; + m.function_meta(FlatMap::next_back__meta)?; + m.function_meta(FlatMap::size_hint__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.implement_trait::(rune::item!(::std::iter::DoubleEndedIterator))?; + + m.ty::()?; + m.function_meta(Peekable::next__meta)?; + m.function_meta(Peekable::next_back__meta)?; + m.function_meta(Peekable::size_hint__meta)?; + m.function_meta(Peekable::len__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.implement_trait::(rune::item!(::std::iter::DoubleEndedIterator))?; + m.implement_trait::(rune::item!(::std::iter::ExactSizeIterator))?; + m.function_meta(Peekable::peek__meta)?; + + m.ty::()?; + m.function_meta(Skip::next__meta)?; + m.function_meta(Skip::next_back__meta)?; + m.function_meta(Skip::size_hint__meta)?; + m.function_meta(Skip::len__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.implement_trait::(rune::item!(::std::iter::DoubleEndedIterator))?; + m.implement_trait::(rune::item!(::std::iter::ExactSizeIterator))?; + + m.ty::()?; + m.function_meta(Take::next__meta)?; + m.function_meta(Take::next_back__meta)?; + m.function_meta(Take::size_hint__meta)?; + m.function_meta(Take::len__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.implement_trait::(rune::item!(::std::iter::DoubleEndedIterator))?; + m.implement_trait::(rune::item!(::std::iter::ExactSizeIterator))?; + + { + let mut t = m.define_trait(["ExactSizeIterator"])?; + + t.docs(docstring! { + /// An iterator that knows its exact length. + /// + /// Many [`Iterator`]s don't know how many times they will iterate, but some do. + /// If an iterator knows how many times it can iterate, providing access to + /// that information can be useful. For example, if you want to iterate + /// backwards, a good start is to know where the end is. + /// + /// When implementing an `ExactSizeIterator`, you must also implement + /// [`Iterator`]. When doing so, the implementation of [`Iterator::size_hint`] + /// *must* return the exact size of the iterator. + /// + /// The [`len`] method has a default implementation, so you usually shouldn't + /// implement it. However, you may be able to provide a more performant + /// implementation than the default, so overriding it in this case makes sense. + /// + /// Note that this trait is a safe trait and as such does *not* and *cannot* + /// guarantee that the returned length is correct. This means that `unsafe` + /// code **must not** rely on the correctness of [`Iterator::size_hint`]. The + /// unstable and unsafe [`TrustedLen`](super::marker::TrustedLen) trait gives + /// this additional guarantee. + /// + /// [`len`]: ExactSizeIterator::len + /// + /// # When *shouldn't* an adapter be `ExactSizeIterator`? + /// + /// If an adapter makes an iterator *longer*, then it's usually incorrect for + /// that adapter to implement `ExactSizeIterator`. The inner exact-sized + /// iterator might already be `usize::MAX`-long, and thus the length of the + /// longer adapted iterator would no longer be exactly representable in `usize`. + /// + /// This is why [`Chain`](crate::iter::Chain) isn't `ExactSizeIterator`, + /// even when `A` and `B` are both `ExactSizeIterator`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // a finite range knows exactly how many times it will iterate + /// let five = (0..5).iter(); + /// + /// assert_eq!(5, five.len()); + /// ``` + })?; + + t.handler(|cx| { + let len = cx.find(Protocol::LEN)?; + cx.function_handler("len", &len)?; + Ok(()) + })?; + + t.function("len")? + .argument_types::<(Value,)>()? + .return_type::()? + .docs(docstring! { + /// Returns the exact remaining length of the iterator. + /// + /// The implementation ensures that the iterator will return + /// exactly `len()` more times a [`Some(T)`] value, before + /// returning [`None`]. This method has a default + /// implementation, so you usually should not implement it + /// directly. However, if you can provide a more efficient + /// implementation, you can do so. See the [trait-level] docs + /// for an example. + /// + /// This function has the same safety guarantees as the + /// [`Iterator::size_hint`] function. + /// + /// [trait-level]: ExactSizeIterator + /// [`Some(T)`]: Some + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // a finite range knows exactly how many times it will iterate + /// let range = (0..5).iter(); + /// + /// assert_eq!(5, range.len()); + /// let _ = range.next(); + /// assert_eq!(4, range.len()); + /// ``` + })?; + } + + { + let mut t = m.define_trait(["Iterator"])?; + + t.docs(docstring! { + /// A trait for dealing with iterators. + })?; + + t.handler(|cx| { + let next = cx.find(Protocol::NEXT)?; + cx.function_handler("next", &next)?; + + let size_hint = if let Some(size_hint) = cx.try_find(Protocol::SIZE_HINT)? { + cx.function_handler("size_hint", &size_hint)?; + size_hint + } else { + let size_hint = cx.function("size_hint", |_: Value| (0usize, None::))?; + cx.function_handler(Protocol::SIZE_HINT, &size_hint)?; + size_hint + }; + + let next = Caller::>::new(next); + let size_hint = Caller::<(usize, Option)>::new(size_hint); + + if let Some(nth) = cx.try_find(Protocol::NTH)? { + cx.function_handler("nth", &nth)?; + } else { + let next = next.clone(); + + let nth = cx.function("nth", move |iterator: Value, mut n: usize| loop { + let Some(value) = vm_try!(next.call((&iterator,))) else { + break VmResult::Ok(None); + }; + + if n == 0 { + break VmResult::Ok(Some(value)); + } + + n -= 1; + })?; + + cx.function_handler(Protocol::NTH, &nth)?; + } + + cx.function(Protocol::INTO_ITER, |value: Value| value)?; + + { + let next = next.clone(); + + cx.function("count", move |iter: Value| { + let mut n = 0usize; + + loop { + if vm_try!(next.call((&iter,))).is_none() { + break VmResult::Ok(n); + }; + + n += 1; + } + })?; + } + + { + let next = next.clone(); + + cx.function("fold", move |iter: Value, mut acc: Value, f: Function| { + loop { + let Some(value) = vm_try!(next.call((&iter,))) else { + break VmResult::Ok(acc); + }; + + acc = vm_try!(f.call((acc, value))); + } + })?; + } + + { + let next = next.clone(); + + cx.function("reduce", move |iter: Value, f: Function| { + let Some(mut acc) = vm_try!(next.call((&iter,))) else { + return VmResult::Ok(None); + }; + + while let Some(value) = vm_try!(next.call((&iter,))) { + acc = vm_try!(f.call((acc, value))); + } + + VmResult::Ok(Some(acc)) + })?; + } + + { + let next = next.clone(); + + cx.function("find", move |iter: Value, f: Function| loop { + let Some(value) = vm_try!(next.call((&iter,))) else { + break VmResult::Ok(None); + }; + + if vm_try!(f.call::((value.clone(),))) { + break VmResult::Ok(Some(value)); + } + })?; + } + + { + let next = next.clone(); + + cx.function("any", move |iter: Value, f: Function| loop { + let Some(value) = vm_try!(next.call((&iter,))) else { + break VmResult::Ok(false); + }; + + if vm_try!(f.call::((value.clone(),))) { + break VmResult::Ok(true); + } + })?; + } + + { + let next = next.clone(); + + cx.function("all", move |iter: Value, f: Function| loop { + let Some(value) = vm_try!(next.call((&iter,))) else { + break VmResult::Ok(true); + }; + + if !vm_try!(f.call::((value.clone(),))) { + break VmResult::Ok(false); + } + })?; + } + + { + cx.function("chain", |a: Value, b: Value| { + let b = vm_try!(b.protocol_into_iter()); + + VmResult::Ok(Chain { + a: Some(a.clone()), + b: Some(b.clone()), + }) + })?; + cx.function("enumerate", move |iter: Value| Enumerate { iter, count: 0 })?; + cx.function("filter", move |iter: Value, f: Function| Filter { iter, f })?; + cx.function("map", move |iter: Value, f: Function| Map { + iter: Some(iter), + f, + })?; + cx.function("filter_map", move |iter: Value, f: Function| FilterMap { + iter: Some(iter), + f, + })?; + cx.function("flat_map", move |iter: Value, f: Function| FlatMap { + map: Map { + iter: Some(iter), + f, + }, + frontiter: None, + backiter: None, + })?; + cx.function("peekable", move |iter: Value| Peekable { + iter, + peeked: None, + })?; + cx.function("skip", move |iter: Value, n: usize| Skip { iter, n })?; + cx.function("take", move |iter: Value, n: usize| Take { iter, n })?; + } + + { + let next = next.clone(); + let size_hint = size_hint.clone(); + + cx.function( + Params::new("collect", [Vec::type_hash()]), + move |iter: Value| { + let (cap, _) = vm_try!(size_hint.call((&iter,))); + let mut vec = vm_try!(Vec::with_capacity(cap)); + + while let Some(value) = vm_try!(next.call((&iter,))) { + vm_try!(vec.push(value)); + } + + VmResult::Ok(vec) + }, + )?; + } + + { + let next = next.clone(); + let size_hint = size_hint.clone(); + + cx.function( + Params::new("collect", [VecDeque::type_hash()]), + move |iter: Value| { + let (cap, _) = vm_try!(size_hint.call((&iter,))); + let mut vec = vm_try!(Vec::with_capacity(cap)); + + while let Some(value) = vm_try!(next.call((&iter,))) { + vm_try!(vec.push(value)); + } + + VmResult::Ok(VecDeque::from(vec)) + }, + )?; + } + + { + let next = next.clone(); + let size_hint = size_hint.clone(); + + cx.function( + Params::new("collect", [HashSet::type_hash()]), + move |iter: Value| { + let (cap, _) = vm_try!(size_hint.call((&iter,))); + let mut set = vm_try!(HashSet::with_capacity(cap)); + + while let Some(value) = vm_try!(next.call((&iter,))) { + vm_try!(set.insert(value)); + } + + VmResult::Ok(set) + }, + )?; + } + + { + let next = next.with_return::>(); + let size_hint = size_hint.clone(); + + cx.function( + Params::new("collect", [HashMap::type_hash()]), + move |iter: Value| { + let (cap, _) = vm_try!(size_hint.call((&iter,))); + let mut map = vm_try!(HashMap::with_capacity(cap)); + + while let Some((key, value)) = vm_try!(next.call((&iter,))) { + vm_try!(map.insert(key, value)); + } + + VmResult::Ok(map) + }, + )?; + } + + { + let next = next.with_return::>(); + let size_hint = size_hint.clone(); + + cx.function( + Params::new("collect", [Object::type_hash()]), + move |iter: Value| { + let (cap, _) = vm_try!(size_hint.call((&iter,))); + let mut map = vm_try!(Object::with_capacity(cap)); + + while let Some((key, value)) = vm_try!(next.call((&iter,))) { + vm_try!(map.insert(key, value)); + } + + VmResult::Ok(map) + }, + )?; + } + + { + let next = next.clone(); + let size_hint = size_hint.clone(); + + cx.function( + Params::new("collect", [OwnedTuple::type_hash()]), + move |iter: Value| { + let (cap, _) = vm_try!(size_hint.call((&iter,))); + let mut vec = vm_try!(alloc::Vec::try_with_capacity(cap)); + + while let Some(value) = vm_try!(next.call((&iter,))) { + vm_try!(vec.try_push(value)); + } + + VmResult::Ok(vm_try!(OwnedTuple::try_from(vec))) + }, + )?; + } + + { + let next = next.clone(); + + cx.function( + Params::new("collect", [String::type_hash()]), + move |iter: Value| { + let mut string = String::new(); + + while let Some(value) = vm_try!(next.call((&iter,))) { + match &*vm_try!(value.borrow_kind_ref()) { + ValueKind::Char(c) => { + vm_try!(string.try_push(*c)); + } + ValueKind::String(s) => { + vm_try!(string.try_push_str(s)); + } + value => { + return VmResult::expected::(value.type_info()); + } + } + } + + VmResult::Ok(string) + }, + )?; + } + + macro_rules! ops { + ($ty:ty) => {{ + cx.function( + Params::new("product", [<$ty>::type_hash()]), + |iter: Value| { + let mut product = match vm_try!(iter.protocol_next()) { + Some(init) => vm_try!(<$ty>::from_value(init)), + None => <$ty>::ONE, + }; + + while let Some(v) = vm_try!(iter.protocol_next()) { + let v = vm_try!(<$ty>::from_value(v)); + + let Some(out) = product.checked_mul(v) else { + return VmResult::err(VmErrorKind::Overflow); + }; + + product = out; + } + + VmResult::Ok(product) + }, + )?; + } + + { + cx.function(Params::new("sum", [<$ty>::type_hash()]), |iter: Value| { + let mut sum = match vm_try!(iter.protocol_next()) { + Some(init) => vm_try!(<$ty>::from_value(init)), + None => <$ty>::ZERO, + }; + + while let Some(v) = vm_try!(iter.protocol_next()) { + let v = vm_try!(<$ty>::from_value(v)); + + let Some(out) = sum.checked_add(v) else { + return VmResult::err(VmErrorKind::Overflow); + }; + + sum = out; + } + + VmResult::Ok(sum) + })?; + }}; + } + + ops!(i64); + ops!(u8); + ops!(f64); + Ok(()) + })?; + + t.function("next")? + .argument_types::<(Value,)>()? + .return_type::>()? + .docs(docstring! { + /// Advances the iterator and returns the next value. + /// + /// Returns [`None`] when iteration is finished. Individual iterator + /// implementations may choose to resume iteration, and so calling `next()` + /// again may or may not eventually start returning [`Some(Item)`] again at some + /// point. + /// + /// [`Some(Item)`]: Some + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let a = [1, 2, 3]; + /// + /// let iter = a.iter(); + /// + /// // A call to next() returns the next value... + /// assert_eq!(Some(1), iter.next()); + /// assert_eq!(Some(2), iter.next()); + /// assert_eq!(Some(3), iter.next()); + /// + /// // ... and then None once it's over. + /// assert_eq!(None, iter.next()); + /// + /// // More calls may or may not return `None`. Here, they always will. + /// assert_eq!(None, iter.next()); + /// assert_eq!(None, iter.next()); + /// ``` + })?; + + t.function("nth")? + .argument_types::<(Value, usize)>()? + .return_type::>()? + .docs(docstring! { + /// Returns the `n`th element of the iterator. + /// + /// Like most indexing operations, the count starts from zero, so `nth(0)` + /// returns the first value, `nth(1)` the second, and so on. + /// + /// Note that all preceding elements, as well as the returned element, will be + /// consumed from the iterator. That means that the preceding elements will be + /// discarded, and also that calling `nth(0)` multiple times on the same iterator + /// will return different elements. + /// + /// `nth()` will return [`None`] if `n` is greater than or equal to the length of the + /// iterator. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let a = [1, 2, 3]; + /// assert_eq!(a.iter().nth(1), Some(2)); + /// ``` + /// + /// Calling `nth()` multiple times doesn't rewind the iterator: + /// + /// ```rune + /// let a = [1, 2, 3]; + /// + /// let iter = a.iter(); + /// + /// assert_eq!(iter.nth(1), Some(2)); + /// assert_eq!(iter.nth(1), None); + /// ``` + /// + /// Returning `None` if there are less than `n + 1` elements: + /// + /// ``` + /// let a = [1, 2, 3]; + /// assert_eq!(a.iter().nth(10), None); + /// ``` + })?; + + t.function("size_hint")? + .argument_types::<(Value,)>()? + .return_type::<(usize, Option)>()? + .docs(docstring! { + /// Returns the bounds on the remaining length of the iterator. + /// + /// Specifically, `size_hint()` returns a tuple where the first element + /// is the lower bound, and the second element is the upper bound. + /// + /// The second half of the tuple that is returned is an + /// [Option]<[i64]>. A [`None`] here means that either there is no + /// known upper bound, or the upper bound is larger than [`i64`]. + /// + /// # Implementation notes + /// + /// It is not enforced that an iterator implementation yields the declared + /// number of elements. A buggy iterator may yield less than the lower bound or + /// more than the upper bound of elements. + /// + /// `size_hint()` is primarily intended to be used for optimizations such as + /// reserving space for the elements of the iterator, but must not be trusted to + /// e.g., omit bounds checks in unsafe code. An incorrect implementation of + /// `size_hint()` should not lead to memory safety violations. + /// + /// That said, the implementation should provide a correct estimation, because + /// otherwise it would be a violation of the trait's protocol. + /// + /// The default implementation returns (0, [None]) which is correct + /// for any iterator. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let a = [1, 2, 3]; + /// let iter = a.iter(); + /// + /// assert_eq!((3, Some(3)), iter.size_hint()); + /// let _ = iter.next(); + /// assert_eq!((2, Some(2)), iter.size_hint()); + /// ``` + /// + /// A more complex example: + /// + /// ```rune + /// // The even numbers in the range of zero to nine. + /// let iter = (0..10).iter().filter(|x| x % 2 == 0); + /// + /// // We might iterate from zero to ten times. Knowing that it's five + /// // exactly wouldn't be possible without executing filter(). + /// assert_eq!((0, Some(10)), iter.size_hint()); + /// + /// // Let's add five more numbers with chain() + /// let iter = (0..10).iter().filter(|x| x % 2 == 0).chain(15..20); + /// + /// // now both bounds are increased by five + /// assert_eq!((5, Some(15)), iter.size_hint()); + /// ``` + /// + /// Returning `None` for an upper bound: + /// + /// ```rune + /// // an infinite iterator has no upper bound + /// // and the maximum possible lower bound + /// let iter = (0..).iter(); + /// + /// assert_eq!((i64::MAX, None), iter.size_hint()); + /// ``` + })?; + + t.function("count")? + .argument_types::<(Value,)>()? + .return_type::()? + .docs(docstring! { + /// Consumes the iterator, counting the number of iterations and returning it. + /// + /// This method will call [`next`] repeatedly until [`None`] is encountered, + /// returning the number of times it saw [`Some`]. Note that [`next`] has to be + /// called at least once even if the iterator does not have any elements. + /// + /// [`next`]: Iterator::next + /// + /// # Overflow Behavior + /// + /// The method does no guarding against overflows, so counting elements of an + /// iterator with more than [`i64::MAX`] elements panics. + /// + /// # Panics + /// + /// This function might panic if the iterator has more than [`i64::MAX`] + /// elements. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let a = [1, 2, 3]; + /// assert_eq!(a.iter().count(), 3); + /// + /// let a = [1, 2, 3, 4, 5]; + /// assert_eq!(a.iter().count(), 5); + /// ``` + })?; + + t.function("fold")? + .argument_types::<(Value, Value, Function)>()? + .return_type::()? + .docs(docstring! { + /// Folds every element into an accumulator by applying an operation, returning + /// the final result. + /// + /// `fold()` takes two arguments: an initial value, and a closure with two + /// arguments: an 'accumulator', and an element. The closure returns the value + /// that the accumulator should have for the next iteration. + /// + /// The initial value is the value the accumulator will have on the first call. + /// + /// After applying this closure to every element of the iterator, `fold()` + /// returns the accumulator. + /// + /// This operation is sometimes called 'reduce' or 'inject'. + /// + /// Folding is useful whenever you have a collection of something, and want to + /// produce a single value from it. + /// + /// Note: `fold()`, and similar methods that traverse the entire iterator, might + /// not terminate for infinite iterators, even on traits for which a result is + /// determinable in finite time. + /// + /// Note: [`reduce()`] can be used to use the first element as the initial + /// value, if the accumulator type and item type is the same. + /// + /// Note: `fold()` combines elements in a *left-associative* fashion. For + /// associative operators like `+`, the order the elements are combined in is + /// not important, but for non-associative operators like `-` the order will + /// affect the final result. For a *right-associative* version of `fold()`, see + /// [`DoubleEndedIterator::rfold()`]. + /// + /// # Note to Implementors + /// + /// Several of the other (forward) methods have default implementations in + /// terms of this one, so try to implement this explicitly if it can + /// do something better than the default `for` loop implementation. + /// + /// In particular, try to have this call `fold()` on the internal parts + /// from which this iterator is composed. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let a = [1, 2, 3]; + /// + /// // the sum of all of the elements of the array + /// let sum = a.iter().fold(0, |acc, x| acc + x); + /// + /// assert_eq!(sum, 6); + /// ``` + /// + /// Let's walk through each step of the iteration here: + /// + /// | element | acc | x | result | + /// |---------|-----|---|--------| + /// | | 0 | | | + /// | 1 | 0 | 1 | 1 | + /// | 2 | 1 | 2 | 3 | + /// | 3 | 3 | 3 | 6 | + /// + /// And so, our final result, `6`. + /// + /// This example demonstrates the left-associative nature of `fold()`: + /// it builds a string, starting with an initial value + /// and continuing with each element from the front until the back: + /// + /// ```rune + /// let numbers = [1, 2, 3, 4, 5]; + /// + /// let zero = "0"; + /// + /// let result = numbers.iter().fold(zero, |acc, x| { + /// format!("({} + {})", acc, x) + /// }); + /// + /// assert_eq!(result, "(((((0 + 1) + 2) + 3) + 4) + 5)"); + /// ``` + /// + /// It's common for people who haven't used iterators a lot to + /// use a `for` loop with a list of things to build up a result. Those + /// can be turned into `fold()`s: + /// + /// ```rune + /// let numbers = [1, 2, 3, 4, 5]; + /// + /// let result = 0; + /// + /// // for loop: + /// for i in numbers { + /// result = result + i; + /// } + /// + /// // fold: + /// let result2 = numbers.iter().fold(0, |acc, x| acc + x); + /// + /// // they're the same + /// assert_eq!(result, result2); + /// ``` + /// + /// [`reduce()`]: Iterator::reduce + })?; + + t.function("reduce")? + .argument_types::<(Value, Function)>()? + .return_type::>()? + .docs(docstring! { + /// Reduces the elements to a single one, by repeatedly applying a reducing + /// operation. + /// + /// If the iterator is empty, returns [`None`]; otherwise, returns the result of + /// the reduction. + /// + /// The reducing function is a closure with two arguments: an 'accumulator', and + /// an element. For iterators with at least one element, this is the same as + /// [`fold()`] with the first element of the iterator as the initial accumulator + /// value, folding every subsequent element into it. + /// + /// [`fold()`]: Iterator::fold + /// + /// # Example + /// + /// ```rune + /// let reduced = (1..10).iter().reduce(|acc, e| acc + e).unwrap(); + /// assert_eq!(reduced, 45); + /// + /// // Which is equivalent to doing it with `fold`: + /// let folded = (1..10).iter().fold(0, |acc, e| acc + e); + /// assert_eq!(reduced, folded); + /// ``` + })?; + + t.function("find")? + .argument_types::<(Value, Function)>()? + .return_type::>()? + .docs(docstring! { + /// Searches for an element of an iterator that satisfies a predicate. + /// + /// `find()` takes a closure that returns `true` or `false`. It applies this + /// closure to each element of the iterator, and if any of them return `true`, + /// then `find()` returns [`Some(element)`]. If they all return `false`, it + /// returns [`None`]. + /// + /// `find()` is short-circuiting; in other words, it will stop processing as + /// soon as the closure returns `true`. + /// + /// If you need the index of the element, see [`position()`]. + /// + /// [`Some(element)`]: Some + /// [`position()`]: Iterator::position + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let a = [1, 2, 3]; + /// + /// assert_eq!(a.iter().find(|x| x == 2), Some(2)); + /// + /// assert_eq!(a.iter().find(|x| x == 5), None); + /// ``` + /// + /// Stopping at the first `true`: + /// + /// ```rune + /// let a = [1, 2, 3]; + /// + /// let iter = a.iter(); + /// + /// assert_eq!(iter.find(|x| x == 2), Some(2)); + /// + /// // we can still use `iter`, as there are more elements. + /// assert_eq!(iter.next(), Some(3)); + /// ``` + /// + /// Note that `iter.find(f)` is equivalent to `iter.filter(f).next()`. + })?; + + t.function("any")? + .argument_types::<(Value, Function)>()? + .return_type::()? + .docs(docstring! { + /// Tests if any element of the iterator matches a predicate. + /// + /// `any()` takes a closure that returns `true` or `false`. It applies this + /// closure to each element of the iterator, and if any of them return `true`, + /// then so does `any()`. If they all return `false`, it returns `false`. + /// + /// `any()` is short-circuiting; in other words, it will stop processing as soon + /// as it finds a `true`, given that no matter what else happens, the result + /// will also be `true`. + /// + /// An empty iterator returns `false`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let a = [1, 2, 3]; + /// + /// assert!(a.iter().any(|x| x > 0)); + /// + /// assert!(!a.iter().any(|x| x > 5)); + /// ``` + /// + /// Stopping at the first `true`: + /// + /// ```rune + /// let a = [1, 2, 3]; + /// + /// let iter = a.iter(); + /// + /// assert!(iter.any(|x| x != 2)); + /// + /// // we can still use `iter`, as there are more elements. + /// assert_eq!(iter.next(), Some(2)); + /// ``` + })?; + + t.function("all")? + .argument_types::<(Value, Function)>()? + .return_type::()? + .docs(docstring! { + /// Tests if every element of the iterator matches a predicate. + /// + /// `all()` takes a closure that returns `true` or `false`. It applies this + /// closure to each element of the iterator, and if they all return `true`, then + /// so does `all()`. If any of them return `false`, it returns `false`. + /// + /// `all()` is short-circuiting; in other words, it will stop processing as soon + /// as it finds a `false`, given that no matter what else happens, the result + /// will also be `false`. + /// + /// An empty iterator returns `true`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let a = [1, 2, 3]; + /// + /// assert!(a.iter().all(|x| x > 0)); + /// + /// assert!(!a.iter().all(|x| x > 2)); + /// ``` + /// + /// Stopping at the first `false`: + /// + /// ```rune + /// let a = [1, 2, 3]; + /// + /// let iter = a.iter(); + /// + /// assert!(!iter.all(|x| x != 2)); + /// + /// // we can still use `iter`, as there are more elements. + /// assert_eq!(iter.next(), Some(3)); + /// ``` + })?; + + t.function("chain")? + .argument_types::<(Value, Value)>()? + .return_type::()? + .docs(docstring! { + /// Takes two iterators and creates a new iterator over both in sequence. + /// + /// `chain()` will return a new iterator which will first iterate over + /// values from the first iterator and then over values from the second + /// iterator. + /// + /// In other words, it links two iterators together, in a chain. 🔗 + /// + /// [`once`] is commonly used to adapt a single value into a chain of other + /// kinds of iteration. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let a1 = [1, 2, 3]; + /// let a2 = [4, 5, 6]; + /// + /// let iter = a1.iter().chain(a2.iter()); + /// + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), Some(4)); + /// assert_eq!(iter.next(), Some(5)); + /// assert_eq!(iter.next(), Some(6)); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// Since the argument to `chain()` uses [`INTO_ITER`], we can pass anything + /// that can be converted into an [`Iterator`], not just an [`Iterator`] itself. + /// For example, slices (`[T]`) implement [`INTO_ITER`], and so can be passed to + /// `chain()` directly: + /// + /// ```rune + /// let s1 = [1, 2, 3]; + /// let s2 = [4, 5, 6]; + /// + /// let iter = s1.iter().chain(s2); + /// + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), Some(4)); + /// assert_eq!(iter.next(), Some(5)); + /// assert_eq!(iter.next(), Some(6)); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// [`INTO_ITER`]: protocol@INTO_ITER + })?; + + t.function("enumerate")? + .argument_types::<(Value,)>()? + .return_type::()? + .docs(docstring! { + /// Creates an iterator which gives the current iteration count as well as + /// the next value. + /// + /// The iterator returned yields pairs `(i, val)`, where `i` is the current + /// index of iteration and `val` is the value returned by the iterator. + /// + /// `enumerate()` keeps its count as a usize. If you want to count by a + /// different sized integer, the zip function provides similar + /// functionality. + /// + /// # Examples + /// + /// ```rune + /// let a = ['a', 'b', 'c']; + /// + /// let iter = a.iter().enumerate(); + /// + /// assert_eq!(iter.next(), Some((0, 'a'))); + /// assert_eq!(iter.next(), Some((1, 'b'))); + /// assert_eq!(iter.next(), Some((2, 'c'))); + /// assert_eq!(iter.next(), None); + /// ``` + })?; + + t.function("filter")? + .argument_types::<(Value, Function)>()? + .return_type::()? + .docs(docstring! { + /// Creates an iterator which uses a closure to determine if an element + /// should be yielded. + /// + /// Given an element the closure must return `true` or `false`. The returned + /// iterator will yield only the elements for which the closure returns + /// `true`. + /// + /// ```rune + /// let a = [0, 1, 2]; + /// + /// let iter = a.iter().filter(|x| x.is_positive()); + /// + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), None); + /// ``` + })?; + + t.function("map")? + .argument_types::<(Value, Function)>()? + .return_type::()? + .docs(docstring! { + /// Takes a closure and creates an iterator which calls that closure on each + /// element. + /// + /// `map()` transforms one iterator into another. It produces a new iterator + /// which calls this closure on each element of the original iterator. + /// + /// If you are good at thinking in types, you can think of `map()` like + /// this: If you have an iterator that gives you elements of some type `A`, + /// and you want an iterator of some other type `B`, you can use `map()`, + /// passing a closure that takes an `A` and returns a `B`. + /// + /// `map()` is conceptually similar to a `for` loop. However, as `map()` is + /// lazy, it is best used when you're already working with other iterators. + /// If you're doing some sort of looping for a side effect, it's considered + /// more idiomatic to use `for` than `map()`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let a = [1, 2, 3]; + /// + /// let iter = a.iter().map(|x| 2 * x); + /// + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(4)); + /// assert_eq!(iter.next(), Some(6)); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// If you're doing some sort of side effect, prefer `for` to `map()`: + /// + /// ```rune + /// // don't do this: + /// (0..5).iter().map(|x| println!("{}", x)); + /// + /// // it won't even execute, as it is lazy. Rust will warn you about this. + /// + /// // Instead, use for: + /// for x in 0..5 { + /// println!("{}", x); + /// } + /// ``` + })?; + + t.function("filter_map")? + .argument_types::<(Value, Function)>()? + .return_type::()? + .docs(docstring! { + /// Creates an iterator that both filters and maps. + /// + /// The returned iterator yields only the `value`s for which the supplied + /// closure returns `Some(value)`. + /// + /// `filter_map` can be used to make chains of [`filter`] and [`map`] more + /// concise. The example below shows how a `map().filter().map()` can be + /// shortened to a single call to `filter_map`. + /// + /// [`filter`]: Iterator::filter + /// [`map`]: Iterator::map + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let a = ["1", "two", "NaN", "four", "5"]; + /// + /// let iter = a.iter().filter_map(|s| s.parse::().ok()); + /// + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(5)); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// Here's the same example, but with [`filter`] and [`map`]: + /// + /// ```rune + /// let a = ["1", "two", "NaN", "four", "5"]; + /// let iter = a.iter().map(|s| s.parse::()).filter(|s| s.is_ok()).map(|s| s.unwrap()); + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(5)); + /// assert_eq!(iter.next(), None); + /// ``` + })?; + + t.function("flat_map")? + .argument_types::<(Value, Function)>()? + .return_type::()? + .docs(docstring! { + /// Creates an iterator that works like map, but flattens nested structure. + /// + /// The [`map`] adapter is very useful, but only when the closure argument + /// produces values. If it produces an iterator instead, there's an extra + /// layer of indirection. `flat_map()` will remove this extra layer on its + /// own. + /// + /// You can think of `flat_map(f)` as the semantic equivalent of + /// [`map`]ping, and then [`flatten`]ing as in `map(f).flatten()`. + /// + /// Another way of thinking about `flat_map()`: [`map`]'s closure returns + /// one item for each element, and `flat_map()`'s closure returns an + /// iterator for each element. + /// + /// [`map`]: Iterator::map + /// [`flatten`]: Iterator::flatten + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let words = ["alpha", "beta", "gamma"]; + /// + /// // chars() returns an iterator + /// let merged = words.iter().flat_map(|s| s.chars()).collect::(); + /// assert_eq!(merged, "alphabetagamma"); + /// ``` + })?; + + t.function("peekable")? + .argument_types::<(Value,)>()? + .return_type::()? + .docs(docstring! { + /// Creates an iterator which can use the [`peek`] method to look at the next + /// element of the iterator without consuming it. See their documentation for + /// more information. + /// + /// Note that the underlying iterator is still advanced when [`peek`] are called + /// for the first time: In order to retrieve the next element, [`next`] is + /// called on the underlying iterator, hence any side effects (i.e. anything + /// other than fetching the next value) of the [`next`] method will occur. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let xs = [1, 2, 3]; + /// + /// let iter = xs.iter().peekable(); + /// + /// // peek() lets us see into the future + /// assert_eq!(iter.peek(), Some(1)); + /// assert_eq!(iter.next(), Some(1)); + /// + /// assert_eq!(iter.next(), Some(2)); + /// + /// // we can peek() multiple times, the iterator won't advance + /// assert_eq!(iter.peek(), Some(3)); + /// assert_eq!(iter.peek(), Some(3)); + /// + /// assert_eq!(iter.next(), Some(3)); + /// + /// // after the iterator is finished, so is peek() + /// assert_eq!(iter.peek(), None); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// [`peek`]: Peekable::peek + /// [`next`]: Iterator::next + })?; + + t.function("skip")? + .argument_types::<(Value, usize)>()? + .return_type::()? + .docs(docstring! { + /// Creates an iterator that skips the first `n` elements. + /// + /// `skip(n)` skips elements until `n` elements are skipped or the end of the + /// iterator is reached (whichever happens first). After that, all the remaining + /// elements are yielded. In particular, if the original iterator is too short, + /// then the returned iterator is empty. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let a = [1, 2, 3]; + /// + /// let iter = a.iter().skip(2); + /// + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), None); + /// ``` + })?; + + t.function("take")? + .argument_types::<(Value, usize)>()? + .return_type::()? + .docs(docstring! { + /// Creates an iterator that yields the first `n` elements, or fewer if the + /// underlying iterator ends sooner. + /// + /// `take(n)` yields elements until `n` elements are yielded or the end of the + /// iterator is reached (whichever happens first). The returned iterator is a + /// prefix of length `n` if the original iterator contains at least `n` + /// elements, otherwise it contains all of the (fewer than `n`) elements of the + /// original iterator. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let a = [1, 2, 3]; + /// + /// let iter = a.iter().take(2); + /// + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// `take()` is often used with an infinite iterator, to make it finite: + /// + /// ```rune + /// let iter = (0..).iter().take(3); + /// + /// assert_eq!(iter.next(), Some(0)); + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// If less than `n` elements are available, `take` will limit itself to the + /// size of the underlying iterator: + /// + /// ```rune + /// let v = [1, 2]; + /// let iter = v.iter().take(5); + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), None); + /// ``` + })?; + + macro_rules! sum_ops { + ($ty:ty) => { + t.function(Params::new("sum", [<$ty>::type_hash()]))? + .argument_types::<(Value,)>()? + .return_type::<$ty>()? + .docs(docstring! { + /// Sums the elements of an iterator. + /// + /// Takes each element, adds them together, and returns the result. + /// + /// An empty iterator returns the zero value of the type. + /// + /// `sum()` can be used to sum numerical built-in types, such as `int`, `float` + /// and `u8`. The first element returned by the iterator determines the type + /// being summed. + /// + /// # Panics + /// + /// When calling `sum()` and a primitive integer type is being returned, this + /// method will panic if the computation overflows. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + #[doc = concat!(" let a = [1", stringify!($ty), ", 2", stringify!($ty), ", 3", stringify!($ty), "];")] + #[doc = concat!(" let sum = a.iter().sum::<", stringify!($ty), ">();")] + /// + #[doc = concat!(" assert_eq!(sum, 6", stringify!($ty), ");")] + /// ``` + })?; + }; + } + + sum_ops!(i64); + sum_ops!(f64); + sum_ops!(u8); + + macro_rules! integer_product_ops { + ($ty:ty) => { + t.function(Params::new("product", [<$ty>::type_hash()]))? + .argument_types::<(Value,)>()? + .return_type::<$ty>()? + .docs(docstring! { + /// Iterates over the entire iterator, multiplying all the elements + /// + /// An empty iterator returns the one value of the type. + /// + /// `sum()` can be used to sum numerical built-in types, such as `int`, `float` + /// and `u8`. The first element returned by the iterator determines the type + /// being multiplied. + /// + /// # Panics + /// + /// When calling `product()` and a primitive integer type is being returned, + /// method will panic if the computation overflows. + /// + /// # Examples + /// + /// ```rune + /// fn factorial(n) { + #[doc = concat!(" (1", stringify!($ty), "..=n).iter().product::<", stringify!($ty), ">()")] + /// } + /// + #[doc = concat!(" assert_eq!(factorial(0", stringify!($ty), "), 1", stringify!($ty), ");")] + #[doc = concat!(" assert_eq!(factorial(1", stringify!($ty), "), 1", stringify!($ty), ");")] + #[doc = concat!(" assert_eq!(factorial(5", stringify!($ty), "), 120", stringify!($ty), ");")] + /// ``` + })?; + }; + } + + t.function(Params::new("collect", [Vec::type_hash()]))? + .return_type::()? + .docs(docstring! { + /// Collect the iterator as a [`Vec`]. + /// + /// # Examples + /// + /// ```rune + /// use std::iter::range; + /// + /// assert_eq!((0..3).iter().collect::(), [0, 1, 2]); + /// ``` + })?; + + t.function(Params::new("collect", [VecDeque::type_hash()]))? + .return_type::()? + .docs(docstring! { + /// Collect the iterator as a [`VecDeque`]. + /// + /// # Examples + /// + /// ```rune + /// use std::collections::VecDeque; + /// + /// assert_eq!((0..3).iter().collect::(), VecDeque::from::([0, 1, 2])); + /// ``` + })?; + + t.function(Params::new("collect", [HashSet::type_hash()]))? + .return_type::()? + .docs(docstring! { + /// Collect the iterator as a [`HashSet`]. + /// + /// # Examples + /// + /// ```rune + /// use std::collections::HashSet; + /// + /// let a = (0..3).iter().collect::(); + /// let b = HashSet::from_iter([0, 1, 2]); + /// + /// assert_eq!(a, b); + /// ``` + })?; + + t.function(Params::new("collect", [HashMap::type_hash()]))? + .return_type::()? + .docs(docstring! { + /// Collect the iterator as a [`HashMap`]. + /// + /// # Examples + /// + /// ```rune + /// use std::collections::HashMap; + /// + /// let actual = (0..3).iter().map(|n| (n, n.to_string())).collect::(); + /// + /// let expected = HashMap::from_iter([ + /// (0, "0"), + /// (1, "1"), + /// (2, "2"), + /// ]); + /// + /// assert_eq!(actual, expected); + /// ``` + })?; + + t.function(Params::new("collect", [Object::type_hash()]))? + .return_type::()? + .docs(docstring! { + /// Collect the iterator as an [`Object`]. + /// + /// # Examples + /// + /// ```rune + /// assert_eq!([("first", 1), ("second", 2)].iter().collect::(), #{first: 1, second: 2}); + /// ``` + })?; + + t.function(Params::new("collect", [OwnedTuple::type_hash()]))? + .return_type::()? + .docs(docstring! { + /// Collect the iterator as a [`Tuple`]. + /// + /// # Examples + /// + /// ```rune + /// assert_eq!((0..3).iter().collect::(), (0, 1, 2)); + /// ``` + })?; + + t.function(Params::new("collect", [String::type_hash()]))? + .return_type::()? + .docs(docstring! { + /// Collect the iterator as a [`String`]. + /// + /// # Examples + /// + /// ```rune + /// assert_eq!(["first", "second"].iter().collect::(), "firstsecond"); + /// ``` + })?; + + macro_rules! float_product_ops { + ($ty:ty) => { + t.function(Params::new("product", [<$ty>::type_hash()]))? + .argument_types::<(Value,)>()? + .return_type::<$ty>()? + .docs(docstring! { + /// Iterates over the entire iterator, multiplying all the elements + /// + /// An empty iterator returns the one value of the type. + /// + /// `sum()` can be used to sum numerical built-in types, such as `int`, `float` + /// and `u8`. The first element returned by the iterator determines the type + /// being multiplied. + /// + /// # Panics + /// + /// When calling `product()` and a primitive integer type is being returned, + /// method will panic if the computation overflows. + /// + /// # Examples + /// + /// ```rune + /// fn factorial(n) { + #[doc = concat!(" (1..=n).iter().map(|n| n as ", stringify!($ty), ").product::<", stringify!($ty), ">()")] + /// } + /// + #[doc = concat!(" assert_eq!(factorial(0), 1", stringify!($ty), ");")] + #[doc = concat!(" assert_eq!(factorial(1), 1", stringify!($ty), ");")] + #[doc = concat!(" assert_eq!(factorial(5), 120", stringify!($ty), ");")] + /// ``` + })?; + }; + } + + integer_product_ops!(i64); + integer_product_ops!(u8); + float_product_ops!(f64); + } + + { + let mut t = m.define_trait(["DoubleEndedIterator"])?; + + t.docs(docstring! { + /// An iterator able to yield elements from both ends. + /// + /// Something that implements `DoubleEndedIterator` has one extra capability + /// over something that implements [`Iterator`]: the ability to also take + /// `Item`s from the back, as well as the front. + /// + /// It is important to note that both back and forth work on the same range, + /// and do not cross: iteration is over when they meet in the middle. + /// + /// In a similar fashion to the [`Iterator`] protocol, once a + /// `DoubleEndedIterator` returns [`None`] from a [`next_back()`], calling it + /// again may or may not ever return [`Some`] again. [`next()`] and + /// [`next_back()`] are interchangeable for this purpose. + /// + /// [`next_back()`]: DoubleEndedIterator::next_back + /// [`next()`]: Iterator::next + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let numbers = [1, 2, 3, 4, 5, 6]; + /// + /// let iter = numbers.iter(); + /// + /// assert_eq!(Some(1), iter.next()); + /// assert_eq!(Some(6), iter.next_back()); + /// assert_eq!(Some(5), iter.next_back()); + /// assert_eq!(Some(2), iter.next()); + /// assert_eq!(Some(3), iter.next()); + /// assert_eq!(Some(4), iter.next()); + /// assert_eq!(None, iter.next()); + /// assert_eq!(None, iter.next_back()); + /// ``` + })?; + + t.handler(|cx| { + let _ = cx.find(Protocol::NEXT)?; + let next_back = cx.find(Protocol::NEXT_BACK)?; + cx.function_handler("next_back", &next_back)?; + + if let Some(nth) = cx.try_find(Protocol::NTH)? { + cx.function_handler("nth_back", &nth)?; + } else { + let next_back = next_back.clone(); + + let nth = cx.function("nth_back", move |iterator: Value, mut n: usize| loop { + let mut memory = [iterator.clone()]; + vm_try!(next_back( + &mut memory, + InstAddress::ZERO, + 1, + Output::keep(0) + )); + let [value] = memory; + + let Some(value) = vm_try!(Option::::from_value(value)) else { + break VmResult::Ok(None); + }; + + if n == 0 { + break VmResult::Ok(Some(value)); + } + + n -= 1; + })?; + + cx.function_handler(Protocol::NTH, &nth)?; + } + + cx.raw_function("rev", |stack, addr, len, out| { + let [value] = vm_try!(stack.slice_at(addr, len)) else { + return VmResult::err(VmErrorKind::BadArgumentCount { + actual: len, + expected: 1, + }); + }; + + let rev = Rev { + value: value.clone(), + }; + + vm_try!(out.store(stack, || rune::to_value(rev))); + VmResult::Ok(()) + })?; + + Ok(()) + })?; + + t.function("next_back")? + .argument_types::<(Value,)>()? + .return_type::>()? + .docs(docstring! { + /// Removes and returns an element from the end of the iterator. + /// + /// Returns `None` when there are no more elements. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let numbers = [1, 2, 3, 4, 5, 6]; + /// + /// let iter = numbers.iter(); + /// + /// assert_eq!(Some(1), iter.next()); + /// assert_eq!(Some(6), iter.next_back()); + /// assert_eq!(Some(5), iter.next_back()); + /// assert_eq!(Some(2), iter.next()); + /// assert_eq!(Some(3), iter.next()); + /// assert_eq!(Some(4), iter.next()); + /// assert_eq!(None, iter.next()); + /// assert_eq!(None, iter.next_back()); + /// ``` + })?; + + t.function("rev")? + .argument_types::<(Value,)>()? + .return_type::()? + .docs(docstring! { + /// Reverses an iterator's direction. + /// + /// Usually, iterators iterate from left to right. After using `rev()`, an + /// iterator will instead iterate from right to left. + /// + /// This is only possible if the iterator has an end, so `rev()` only works on + /// double-ended iterators. + /// + /// # Examples + /// + /// ```rune + /// let a = [1, 2, 3]; + /// + /// let iter = a.iter().rev(); + /// + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(1)); + /// + /// assert_eq!(iter.next(), None); + /// ``` + })?; + } + + m.function_meta(range)?; + + m.ty::()?; + m.function_meta(Empty::next__meta)?; + m.function_meta(Empty::next_back__meta)?; + m.function_meta(Empty::size_hint__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.implement_trait::(rune::item!(::std::iter::DoubleEndedIterator))?; + m.function_meta(empty)?; + + m.ty::()?; + m.function_meta(Once::next__meta)?; + m.function_meta(Once::next_back__meta)?; + m.function_meta(Once::size_hint__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.implement_trait::(rune::item!(::std::iter::DoubleEndedIterator))?; + m.function_meta(once)?; + Ok(m) } /// Construct an iterator which produces no values. @@ -77,8 +1757,29 @@ pub fn module() -> Result { /// assert_eq!(empty().collect::(), []); /// ``` #[rune::function] -fn empty() -> Iterator { - Iterator::empty() +fn empty() -> Empty { + Empty +} + +#[derive(Any)] +#[rune(item = ::std::iter)] +struct Empty; + +impl Empty { + #[rune::function(keep, protocol = NEXT)] + fn next(&mut self) -> Option { + None + } + + #[rune::function(keep, protocol = NEXT_BACK)] + fn next_back(&mut self) -> Option { + None + } + + #[rune::function(keep, protocol = SIZE_HINT)] + fn size_hint(&self) -> (usize, Option) { + (0, Some(0)) + } } /// Construct an iterator which produces a single `value` once. @@ -92,8 +1793,32 @@ fn empty() -> Iterator { /// assert_eq!(once(42).collect::(), [42]); /// ``` #[rune::function] -fn once(value: Value) -> Iterator { - Iterator::once(value) +fn once(value: Value) -> Once { + Once { value: Some(value) } +} + +#[derive(Any)] +#[rune(item = ::std::iter)] +struct Once { + value: Option, +} + +impl Once { + #[rune::function(keep, protocol = NEXT)] + fn next(&mut self) -> Option { + self.value.take() + } + + #[rune::function(keep, protocol = NEXT_BACK)] + fn next_back(&mut self) -> Option { + self.value.take() + } + + #[rune::function(keep, protocol = SIZE_HINT)] + fn size_hint(&self) -> (usize, Option) { + let len = usize::from(self.value.is_some()); + (len, Some(len)) + } } /// Produce an iterator which starts at the range `start` and ends at the value @@ -107,1099 +1832,668 @@ fn once(value: Value) -> Iterator { /// assert!(range(0, 3).next().is_some()); /// assert_eq!(range(0, 3).collect::(), [0, 1, 2]); /// ``` -#[rune::function] -fn range(start: i64, end: i64) -> Iterator { - Iterator::from_double_ended("std::iter::Range", start..end) +#[rune::function(deprecated = "Use the `..` operator instead")] +fn range(start: i64, end: i64) -> RangeIter { + RangeIter::new(start..end) } -/// Advances the iterator and returns the next value. -/// -/// Returns [`None`] when iteration is finished. Individual iterator -/// implementations may choose to resume iteration, and so calling `next()` -/// again may or may not eventually start returning [`Some(Item)`] again at some -/// point. -/// -/// [`Some(Item)`]: Some -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let a = [1, 2, 3]; -/// -/// let iter = a.iter(); -/// -/// // A call to next() returns the next value... -/// assert_eq!(Some(1), iter.next()); -/// assert_eq!(Some(2), iter.next()); -/// assert_eq!(Some(3), iter.next()); -/// -/// // ... and then None once it's over. -/// assert_eq!(None, iter.next()); -/// -/// // More calls may or may not return `None`. Here, they always will. -/// assert_eq!(None, iter.next()); -/// assert_eq!(None, iter.next()); -/// ``` -#[rune::function(instance)] -#[inline] -pub(crate) fn next(this: &mut Iterator) -> VmResult> { - this.next() +/// Fuse the iterator if the expression is `None`. +macro_rules! fuse { + ($self:ident . $iter:ident . $($call:tt)+) => { + match $self.$iter { + Some(ref mut iter) => match vm_try!(iter.$($call)+) { + None => { + $self.$iter = None; + None + } + item => item, + }, + None => None, + } + }; } -/// Removes and returns an element from the end of the iterator. -/// -/// Returns `None` when there are no more elements. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let numbers = [1, 2, 3, 4, 5, 6]; -/// -/// let iter = numbers.iter(); -/// -/// assert_eq!(Some(1), iter.next()); -/// assert_eq!(Some(6), iter.next_back()); -/// assert_eq!(Some(5), iter.next_back()); -/// assert_eq!(Some(2), iter.next()); -/// assert_eq!(Some(3), iter.next()); -/// assert_eq!(Some(4), iter.next()); -/// assert_eq!(None, iter.next()); -/// assert_eq!(None, iter.next_back()); -/// ``` -#[rune::function(instance)] -#[inline] -pub(crate) fn next_back(this: &mut Iterator) -> VmResult> { - this.next_back() +/// Try an iterator method without fusing, +/// like an inline `.as_mut().and_then(...)` +macro_rules! maybe { + ($self:ident . $iter:ident . $($call:tt)+) => { + match $self.$iter { + Some(ref mut iter) => vm_try!(iter.$($call)+), + None => None, + } + }; } -/// Searches for an element of an iterator that satisfies a predicate. -/// -/// `find()` takes a closure that returns `true` or `false`. It applies this -/// closure to each element of the iterator, and if any of them return `true`, -/// then `find()` returns [`Some(element)`]. If they all return `false`, it -/// returns [`None`]. -/// -/// `find()` is short-circuiting; in other words, it will stop processing as -/// soon as the closure returns `true`. -/// -/// If you need the index of the element, see [`position()`]. -/// -/// [`Some(element)`]: Some -/// [`position()`]: Iterator::position -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let a = [1, 2, 3]; -/// -/// assert_eq!(a.iter().find(|x| x == 2), Some(2)); -/// -/// assert_eq!(a.iter().find(|x| x == 5), None); -/// ``` -/// -/// Stopping at the first `true`: -/// -/// ```rune -/// let a = [1, 2, 3]; -/// -/// let iter = a.iter(); -/// -/// assert_eq!(iter.find(|x| x == 2), Some(2)); -/// -/// // we can still use `iter`, as there are more elements. -/// assert_eq!(iter.next(), Some(3)); -/// ``` -/// -/// Note that `iter.find(f)` is equivalent to `iter.filter(f).next()`. -#[rune::function(instance)] -#[inline] -pub(crate) fn find(this: &mut Iterator, find: Function) -> VmResult> { - this.find(find) +#[derive(Any, Debug)] +#[rune(item = ::std::iter)] +struct Chain { + a: Option, + b: Option, } -/// Tests if any element of the iterator matches a predicate. -/// -/// `any()` takes a closure that returns `true` or `false`. It applies this -/// closure to each element of the iterator, and if any of them return `true`, -/// then so does `any()`. If they all return `false`, it returns `false`. -/// -/// `any()` is short-circuiting; in other words, it will stop processing as soon -/// as it finds a `true`, given that no matter what else happens, the result -/// will also be `true`. -/// -/// An empty iterator returns `false`. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let a = [1, 2, 3]; -/// -/// assert!(a.iter().any(|x| x > 0)); -/// -/// assert!(!a.iter().any(|x| x > 5)); -/// ``` -/// -/// Stopping at the first `true`: -/// -/// ```rune -/// let a = [1, 2, 3]; -/// -/// let iter = a.iter(); -/// -/// assert!(iter.any(|x| x != 2)); -/// -/// // we can still use `iter`, as there are more elements. -/// assert_eq!(iter.next(), Some(2)); -/// ``` -#[rune::function(instance)] -#[inline] -pub fn any(this: &mut Iterator, find: Function) -> VmResult { - this.any(find) +impl Chain { + #[rune::function(keep, protocol = NEXT)] + #[inline] + fn next(&mut self) -> VmResult> { + VmResult::Ok(match fuse!(self.a.protocol_next()) { + None => maybe!(self.b.protocol_next()), + item => item, + }) + } + + #[rune::function(keep, protocol = NEXT_BACK)] + #[inline] + fn next_back(&mut self) -> VmResult> { + VmResult::Ok(match fuse!(self.b.protocol_next_back()) { + None => maybe!(self.a.protocol_next_back()), + item => item, + }) + } + + #[rune::function(keep, protocol = SIZE_HINT)] + #[inline] + fn size_hint(&self) -> VmResult<(usize, Option)> { + match self { + Self { + a: Some(a), + b: Some(b), + } => { + let (a_lower, a_upper) = vm_try!(a.protocol_size_hint()); + let (b_lower, b_upper) = vm_try!(b.protocol_size_hint()); + + let lower = a_lower.saturating_add(b_lower); + + let upper = match (a_upper, b_upper) { + (Some(x), Some(y)) => x.checked_add(y), + _ => None, + }; + + VmResult::Ok((lower, upper)) + } + Self { + a: Some(a), + b: None, + } => a.protocol_size_hint(), + Self { + a: None, + b: Some(b), + } => b.protocol_size_hint(), + Self { a: None, b: None } => VmResult::Ok((0, Some(0))), + } + } + + #[rune::function(keep, protocol = LEN)] + #[inline] + fn len(&self) -> VmResult { + match self { + Self { + a: Some(a), + b: Some(b), + } => { + let a_len = vm_try!(a.protocol_len()); + let b_len = vm_try!(b.protocol_len()); + VmResult::Ok(a_len.saturating_add(b_len)) + } + Self { + a: Some(a), + b: None, + } => a.protocol_len(), + Self { + a: None, + b: Some(b), + } => b.protocol_len(), + Self { a: None, b: None } => VmResult::Ok(0), + } + } } -/// Tests if every element of the iterator matches a predicate. -/// -/// `all()` takes a closure that returns `true` or `false`. It applies this -/// closure to each element of the iterator, and if they all return `true`, then -/// so does `all()`. If any of them return `false`, it returns `false`. -/// -/// `all()` is short-circuiting; in other words, it will stop processing as soon -/// as it finds a `false`, given that no matter what else happens, the result -/// will also be `false`. -/// -/// An empty iterator returns `true`. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let a = [1, 2, 3]; -/// -/// assert!(a.iter().all(|x| x > 0)); -/// -/// assert!(!a.iter().all(|x| x > 2)); -/// ``` -/// -/// Stopping at the first `false`: -/// -/// ```rune -/// let a = [1, 2, 3]; -/// -/// let iter = a.iter(); -/// -/// assert!(!iter.all(|x| x != 2)); -/// -/// // we can still use `iter`, as there are more elements. -/// assert_eq!(iter.next(), Some(3)); -/// ``` -#[rune::function(instance)] -#[inline] -pub fn all(this: &mut Iterator, find: Function) -> VmResult { - this.all(find) +#[derive(Any, Debug)] +#[rune(item = ::std::iter)] +struct Enumerate { + iter: Value, + count: usize, } -/// Takes two iterators and creates a new iterator over both in sequence. -/// -/// `chain()` will return a new iterator which will first iterate over -/// values from the first iterator and then over values from the second -/// iterator. -/// -/// In other words, it links two iterators together, in a chain. 🔗 -/// -/// [`once`] is commonly used to adapt a single value into a chain of other -/// kinds of iteration. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let a1 = [1, 2, 3]; -/// let a2 = [4, 5, 6]; -/// -/// let iter = a1.iter().chain(a2.iter()); -/// -/// assert_eq!(iter.next(), Some(1)); -/// assert_eq!(iter.next(), Some(2)); -/// assert_eq!(iter.next(), Some(3)); -/// assert_eq!(iter.next(), Some(4)); -/// assert_eq!(iter.next(), Some(5)); -/// assert_eq!(iter.next(), Some(6)); -/// assert_eq!(iter.next(), None); -/// ``` -/// -/// Since the argument to `chain()` uses [`INTO_ITER`], we can pass anything -/// that can be converted into an [`Iterator`], not just an [`Iterator`] itself. -/// For example, slices (`[T]`) implement [`INTO_ITER`], and so can be passed to -/// `chain()` directly: -/// -/// ```rune -/// let s1 = [1, 2, 3]; -/// let s2 = [4, 5, 6]; -/// -/// let iter = s1.iter().chain(s2); -/// -/// assert_eq!(iter.next(), Some(1)); -/// assert_eq!(iter.next(), Some(2)); -/// assert_eq!(iter.next(), Some(3)); -/// assert_eq!(iter.next(), Some(4)); -/// assert_eq!(iter.next(), Some(5)); -/// assert_eq!(iter.next(), Some(6)); -/// assert_eq!(iter.next(), None); -/// ``` -/// -/// [`INTO_ITER`]: protocol@INTO_ITER -#[rune::function(instance)] -#[inline] -pub fn chain(this: Iterator, other: Value) -> VmResult { - this.chain(other) -} +impl Enumerate { + #[rune::function(keep, protocol = NEXT)] + #[inline] + fn next(&mut self) -> VmResult> { + if let Some(value) = vm_try!(self.iter.protocol_next()) { + let i = self.count; + self.count += 1; + return VmResult::Ok(Some((i, value))); + } -/// Creates an iterator which uses a closure to determine if an element -/// should be yielded. -/// -/// Given an element the closure must return `true` or `false`. The returned -/// iterator will yield only the elements for which the closure returns -/// `true`. -/// -/// ```rune -/// let a = [0, 1, 2]; -/// -/// let iter = a.iter().filter(|x| x.is_positive()); -/// -/// assert_eq!(iter.next(), Some(1)); -/// assert_eq!(iter.next(), Some(2)); -/// assert_eq!(iter.next(), None); -/// ``` -#[rune::function(instance)] -#[inline] -fn filter(this: Iterator, filter: Function) -> Iterator { - this.filter(filter) -} + VmResult::Ok(None) + } -/// Creates an iterator that both filters and maps. -/// -/// The returned iterator yields only the `value`s for which the supplied -/// closure returns `Some(value)`. -/// -/// `filter_map` can be used to make chains of [`filter`] and [`map`] more -/// concise. The example below shows how a `map().filter().map()` can be -/// shortened to a single call to `filter_map`. -/// -/// [`filter`]: Iterator::filter -/// [`map`]: Iterator::map -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let a = ["1", "two", "NaN", "four", "5"]; -/// -/// let iter = a.iter().filter_map(|s| s.parse::().ok()); -/// -/// assert_eq!(iter.next(), Some(1)); -/// assert_eq!(iter.next(), Some(5)); -/// assert_eq!(iter.next(), None); -/// ``` -/// -/// Here's the same example, but with [`filter`] and [`map`]: -/// -/// ```rune -/// let a = ["1", "two", "NaN", "four", "5"]; -/// let iter = a.iter().map(|s| s.parse::()).filter(|s| s.is_ok()).map(|s| s.unwrap()); -/// assert_eq!(iter.next(), Some(1)); -/// assert_eq!(iter.next(), Some(5)); -/// assert_eq!(iter.next(), None); -/// ``` -#[rune::function(instance)] -#[inline] -fn filter_map(this: Iterator, filter: Function) -> Iterator { - this.filter_map(filter) + #[rune::function(keep, protocol = NEXT_BACK)] + #[inline] + fn next_back(&mut self) -> VmResult> { + if let Some(value) = vm_try!(self.iter.protocol_next_back()) { + let len = vm_try!(self.iter.protocol_len()); + return VmResult::Ok(Some((self.count + len, value))); + } + + VmResult::Ok(None) + } + + #[rune::function(keep, protocol = SIZE_HINT)] + #[inline] + fn size_hint(&self) -> VmResult<(usize, Option)> { + self.iter.protocol_size_hint() + } + + #[rune::function(keep, protocol = LEN)] + #[inline] + fn len(&self) -> VmResult { + self.iter.protocol_len() + } } -/// Takes a closure and creates an iterator which calls that closure on each -/// element. -/// -/// `map()` transforms one iterator into another. It produces a new iterator -/// which calls this closure on each element of the original iterator. -/// -/// If you are good at thinking in types, you can think of `map()` like -/// this: If you have an iterator that gives you elements of some type `A`, -/// and you want an iterator of some other type `B`, you can use `map()`, -/// passing a closure that takes an `A` and returns a `B`. -/// -/// `map()` is conceptually similar to a `for` loop. However, as `map()` is -/// lazy, it is best used when you're already working with other iterators. -/// If you're doing some sort of looping for a side effect, it's considered -/// more idiomatic to use `for` than `map()`. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let a = [1, 2, 3]; -/// -/// let iter = a.iter().map(|x| 2 * x); -/// -/// assert_eq!(iter.next(), Some(2)); -/// assert_eq!(iter.next(), Some(4)); -/// assert_eq!(iter.next(), Some(6)); -/// assert_eq!(iter.next(), None); -/// ``` -/// -/// If you're doing some sort of side effect, prefer `for` to `map()`: -/// -/// ```rune -/// // don't do this: -/// (0..5).iter().map(|x| println!("{}", x)); -/// -/// // it won't even execute, as it is lazy. Rust will warn you about this. -/// -/// // Instead, use for: -/// for x in 0..5 { -/// println!("{}", x); -/// } -/// ``` -#[rune::function(instance)] -#[inline] -fn map(this: Iterator, map: Function) -> Iterator { - this.map(map) +#[derive(Any, Debug)] +#[rune(item = ::std::iter)] +struct Filter { + iter: Value, + f: Function, } -/// Creates an iterator that works like map, but flattens nested structure. -/// -/// The [`map`] adapter is very useful, but only when the closure argument -/// produces values. If it produces an iterator instead, there's an extra -/// layer of indirection. `flat_map()` will remove this extra layer on its -/// own. -/// -/// You can think of `flat_map(f)` as the semantic equivalent of -/// [`map`]ping, and then [`flatten`]ing as in `map(f).flatten()`. -/// -/// Another way of thinking about `flat_map()`: [`map`]'s closure returns -/// one item for each element, and `flat_map()`'s closure returns an -/// iterator for each element. -/// -/// [`map`]: Iterator::map -/// [`flatten`]: Iterator::flatten -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let words = ["alpha", "beta", "gamma"]; -/// -/// // chars() returns an iterator -/// let merged = words.iter().flat_map(|s| s.chars()).collect::(); -/// assert_eq!(merged, "alphabetagamma"); -/// ``` -#[rune::function(instance)] -#[inline] -fn flat_map(this: Iterator, map: Function) -> Iterator { - this.flat_map(map) +impl Filter { + #[rune::function(keep, protocol = NEXT)] + #[inline] + fn next(&mut self) -> VmResult> { + while let Some(value) = vm_try!(self.iter.protocol_next()) { + if vm_try!(self.f.call::((value.clone(),))) { + return VmResult::Ok(Some(value)); + } + } + + VmResult::Ok(None) + } + + #[rune::function(keep, protocol = NEXT_BACK)] + #[inline] + fn next_back(&mut self) -> VmResult> { + while let Some(value) = vm_try!(self.iter.protocol_next_back()) { + if vm_try!(self.f.call::((value.clone(),))) { + return VmResult::Ok(Some(value)); + } + } + + VmResult::Ok(None) + } + + #[rune::function(keep, protocol = SIZE_HINT)] + #[inline] + fn size_hint(&self) -> VmResult<(usize, Option)> { + let (_, hi) = vm_try!(self.iter.protocol_size_hint()); + VmResult::Ok((0, hi)) + } } -/// Creates an iterator which gives the current iteration count as well as -/// the next value. -/// -/// The iterator returned yields pairs `(i, val)`, where `i` is the current -/// index of iteration and `val` is the value returned by the iterator. -/// -/// `enumerate()` keeps its count as a usize. If you want to count by a -/// different sized integer, the zip function provides similar -/// functionality. -/// -/// # Examples -/// -/// ```rune -/// let a = ['a', 'b', 'c']; -/// -/// let iter = a.iter().enumerate(); -/// -/// assert_eq!(iter.next(), Some((0, 'a'))); -/// assert_eq!(iter.next(), Some((1, 'b'))); -/// assert_eq!(iter.next(), Some((2, 'c'))); -/// assert_eq!(iter.next(), None); -/// ``` -#[rune::function(instance)] -#[inline] -fn enumerate(this: Iterator) -> Iterator { - this.enumerate() +#[derive(Any, Debug)] +#[rune(item = ::std::iter)] +struct Map { + iter: Option, + f: Function, } -/// Returns a reference to the `next()` value without advancing the iterator. -/// -/// Like [`next`], if there is a value, it is wrapped in a `Some(T)`. But if the -/// iteration is over, `None` is returned. -/// -/// [`next`]: Iterator::next -/// -/// Because `peek()` returns a reference, and many iterators iterate over -/// references, there can be a possibly confusing situation where the return -/// value is a double reference. You can see this effect in the examples below. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let xs = [1, 2, 3]; -/// -/// let iter = xs.iter().peekable(); -/// -/// // peek() lets us see into the future -/// assert_eq!(iter.peek(), Some(1)); -/// assert_eq!(iter.next(), Some(1)); -/// -/// assert_eq!(iter.next(), Some(2)); -/// -/// // The iterator does not advance even if we `peek` multiple times -/// assert_eq!(iter.peek(), Some(3)); -/// assert_eq!(iter.peek(), Some(3)); -/// -/// assert_eq!(iter.next(), Some(3)); -/// -/// // After the iterator is finished, so is `peek()` -/// assert_eq!(iter.peek(), None); -/// assert_eq!(iter.next(), None); -/// ``` -#[rune::function(instance)] -#[inline] -fn peek(this: &mut Iterator) -> VmResult> { - this.peek() +impl Map { + #[rune::function(keep, protocol = NEXT)] + #[inline] + fn next(&mut self) -> VmResult> { + if let Some(value) = fuse!(self.iter.protocol_next()) { + return VmResult::Ok(Some(vm_try!(self.f.call::((value.clone(),))))); + } + + VmResult::Ok(None) + } + + #[rune::function(keep, protocol = NEXT_BACK)] + #[inline] + fn next_back(&mut self) -> VmResult> { + if let Some(value) = fuse!(self.iter.protocol_next_back()) { + return VmResult::Ok(Some(vm_try!(self.f.call::((value.clone(),))))); + } + + VmResult::Ok(None) + } + + #[rune::function(keep, protocol = SIZE_HINT)] + #[inline] + fn size_hint(&self) -> VmResult<(usize, Option)> { + let Some(iter) = &self.iter else { + return VmResult::Ok((0, Some(0))); + }; + + iter.protocol_size_hint() + } + + #[rune::function(keep, protocol = LEN)] + #[inline] + fn len(&self) -> VmResult { + let Some(iter) = &self.iter else { + return VmResult::Ok(0); + }; + + iter.protocol_len() + } } -/// Creates an iterator which can use the [`peek`] method to look at the next -/// element of the iterator without consuming it. See their documentation for -/// more information. -/// -/// Note that the underlying iterator is still advanced when [`peek`] are called -/// for the first time: In order to retrieve the next element, [`next`] is -/// called on the underlying iterator, hence any side effects (i.e. anything -/// other than fetching the next value) of the [`next`] method will occur. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let xs = [1, 2, 3]; -/// -/// let iter = xs.iter().peekable(); -/// -/// // peek() lets us see into the future -/// assert_eq!(iter.peek(), Some(1)); -/// assert_eq!(iter.next(), Some(1)); -/// -/// assert_eq!(iter.next(), Some(2)); -/// -/// // we can peek() multiple times, the iterator won't advance -/// assert_eq!(iter.peek(), Some(3)); -/// assert_eq!(iter.peek(), Some(3)); -/// -/// assert_eq!(iter.next(), Some(3)); -/// -/// // after the iterator is finished, so is peek() -/// assert_eq!(iter.peek(), None); -/// assert_eq!(iter.next(), None); -/// ``` -/// -/// [`peek`]: Peekable::peek -/// [`next`]: Iterator::next -#[rune::function(instance)] -#[inline] -fn peekable(this: Iterator) -> Iterator { - this.peekable() +#[derive(Any, Debug)] +#[rune(item = ::std::iter)] +struct FilterMap { + iter: Option, + f: Function, } -macro_rules! sum_ops { - ($name:ident, $ty:ty) => { - /// Sums the elements of an iterator. - /// - /// Takes each element, adds them together, and returns the result. - /// - /// An empty iterator returns the zero value of the type. - /// - /// `sum()` can be used to sum numerical built-in types, such as `int`, `float` - /// and `u8`. The first element returned by the iterator determines the type - /// being summed. - /// - /// # Panics - /// - /// When calling `sum()` and a primitive integer type is being returned, this - /// method will panic if the computation overflows. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```rune - #[doc = concat!(" let a = [1", stringify!($ty), ", 2", stringify!($ty), ", 3", stringify!($ty), "];")] - #[doc = concat!(" let sum = a.iter().sum::<", stringify!($ty), ">();")] - /// - #[doc = concat!(" assert_eq!(sum, 6", stringify!($ty), ");")] - /// ``` - #[rune::function(instance, path = sum::<$ty>)] - #[inline] - fn $name(this: Iterator) -> VmResult<$ty> { - this.sum() +impl FilterMap { + #[rune::function(keep, protocol = NEXT)] + #[inline] + fn next(&mut self) -> VmResult> { + while let Some(value) = fuse!(self.iter.protocol_next()) { + if let Some(value) = vm_try!(self.f.call::>((value.clone(),))) { + return VmResult::Ok(Some(value)); + } } - }; -} -sum_ops!(sum_int, i64); -sum_ops!(sum_float, f64); -sum_ops!(sum_byte, u8); - -macro_rules! integer_product_ops { - ($name:ident, $ty:ty) => { - /// Iterates over the entire iterator, multiplying all the elements - /// - /// An empty iterator returns the one value of the type. - /// - /// `sum()` can be used to sum numerical built-in types, such as `int`, `float` - /// and `u8`. The first element returned by the iterator determines the type - /// being multiplied. - /// - /// # Panics - /// - /// When calling `product()` and a primitive integer type is being returned, - /// method will panic if the computation overflows. - /// - /// # Examples - /// - /// ```rune - /// fn factorial(n) { - #[doc = concat!(" (1", stringify!($ty), "..=n).iter().product::<", stringify!($ty), ">()")] - /// } - /// - #[doc = concat!(" assert_eq!(factorial(0", stringify!($ty), "), 1", stringify!($ty), ");")] - #[doc = concat!(" assert_eq!(factorial(1", stringify!($ty), "), 1", stringify!($ty), ");")] - #[doc = concat!(" assert_eq!(factorial(5", stringify!($ty), "), 120", stringify!($ty), ");")] - /// ``` - #[rune::function(instance, path = product::<$ty>)] - #[inline] - fn $name(this: Iterator) -> VmResult<$ty> { - this.product::<$ty>() + VmResult::Ok(None) + } + + #[rune::function(keep, protocol = NEXT_BACK)] + #[inline] + fn next_back(&mut self) -> VmResult> { + while let Some(value) = fuse!(self.iter.protocol_next_back()) { + if let Some(value) = vm_try!(self.f.call::>((value.clone(),))) { + return VmResult::Ok(Some(value)); + } } - }; + + VmResult::Ok(None) + } } -macro_rules! float_product_ops { - ($name:ident, $ty:ty) => { - /// Iterates over the entire iterator, multiplying all the elements - /// - /// An empty iterator returns the one value of the type. - /// - /// `sum()` can be used to sum numerical built-in types, such as `int`, `float` - /// and `u8`. The first element returned by the iterator determines the type - /// being multiplied. - /// - /// # Panics - /// - /// When calling `product()` and a primitive integer type is being returned, - /// method will panic if the computation overflows. - /// - /// # Examples - /// - /// ```rune - /// fn factorial(n) { - #[doc = concat!(" (1..=n).iter().map(|n| n as ", stringify!($ty), ").product::<", stringify!($ty), ">()")] - /// } - /// - #[doc = concat!(" assert_eq!(factorial(0), 1", stringify!($ty), ");")] - #[doc = concat!(" assert_eq!(factorial(1), 1", stringify!($ty), ");")] - #[doc = concat!(" assert_eq!(factorial(5), 120", stringify!($ty), ");")] - /// ``` - #[rune::function(instance, path = product::<$ty>)] - #[inline] - fn $name(this: Iterator) -> VmResult<$ty> { - this.product::<$ty>() - } - }; +#[derive(Any, Debug)] +#[rune(item = ::std::iter)] +struct FlatMap { + map: Map, + frontiter: Option, + backiter: Option, } -integer_product_ops!(product_int, i64); -integer_product_ops!(product_byte, u8); -float_product_ops!(product_float, f64); +impl FlatMap { + #[rune::function(keep, protocol = NEXT)] + #[inline] + fn next(&mut self) -> VmResult> { + loop { + if let Some(iter) = &mut self.frontiter { + match vm_try!(iter.protocol_next()) { + None => self.frontiter = None, + item @ Some(_) => return VmResult::Ok(item), + } + } -/// Folds every element into an accumulator by applying an operation, returning -/// the final result. -/// -/// `fold()` takes two arguments: an initial value, and a closure with two -/// arguments: an 'accumulator', and an element. The closure returns the value -/// that the accumulator should have for the next iteration. -/// -/// The initial value is the value the accumulator will have on the first call. -/// -/// After applying this closure to every element of the iterator, `fold()` -/// returns the accumulator. -/// -/// This operation is sometimes called 'reduce' or 'inject'. -/// -/// Folding is useful whenever you have a collection of something, and want to -/// produce a single value from it. -/// -/// Note: `fold()`, and similar methods that traverse the entire iterator, might -/// not terminate for infinite iterators, even on traits for which a result is -/// determinable in finite time. -/// -/// Note: [`reduce()`] can be used to use the first element as the initial -/// value, if the accumulator type and item type is the same. -/// -/// Note: `fold()` combines elements in a *left-associative* fashion. For -/// associative operators like `+`, the order the elements are combined in is -/// not important, but for non-associative operators like `-` the order will -/// affect the final result. For a *right-associative* version of `fold()`, see -/// [`DoubleEndedIterator::rfold()`]. -/// -/// # Note to Implementors -/// -/// Several of the other (forward) methods have default implementations in -/// terms of this one, so try to implement this explicitly if it can -/// do something better than the default `for` loop implementation. -/// -/// In particular, try to have this call `fold()` on the internal parts -/// from which this iterator is composed. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let a = [1, 2, 3]; -/// -/// // the sum of all of the elements of the array -/// let sum = a.iter().fold(0, |acc, x| acc + x); -/// -/// assert_eq!(sum, 6); -/// ``` -/// -/// Let's walk through each step of the iteration here: -/// -/// | element | acc | x | result | -/// |---------|-----|---|--------| -/// | | 0 | | | -/// | 1 | 0 | 1 | 1 | -/// | 2 | 1 | 2 | 3 | -/// | 3 | 3 | 3 | 6 | -/// -/// And so, our final result, `6`. -/// -/// This example demonstrates the left-associative nature of `fold()`: -/// it builds a string, starting with an initial value -/// and continuing with each element from the front until the back: -/// -/// ```rune -/// let numbers = [1, 2, 3, 4, 5]; -/// -/// let zero = "0"; -/// -/// let result = numbers.iter().fold(zero, |acc, x| { -/// format!("({} + {})", acc, x) -/// }); -/// -/// assert_eq!(result, "(((((0 + 1) + 2) + 3) + 4) + 5)"); -/// ``` -/// -/// It's common for people who haven't used iterators a lot to -/// use a `for` loop with a list of things to build up a result. Those -/// can be turned into `fold()`s: -/// -/// ```rune -/// let numbers = [1, 2, 3, 4, 5]; -/// -/// let result = 0; -/// -/// // for loop: -/// for i in numbers { -/// result = result + i; -/// } -/// -/// // fold: -/// let result2 = numbers.iter().fold(0, |acc, x| acc + x); -/// -/// // they're the same -/// assert_eq!(result, result2); -/// ``` -/// -/// [`reduce()`]: Iterator::reduce -#[rune::function(instance)] -#[inline] -fn fold(this: Iterator, accumulator: Value, f: Function) -> VmResult { - this.fold(accumulator, f) -} + let Some(value) = vm_try!(self.map.next()) else { + return VmResult::Ok(match &mut self.backiter { + Some(backiter) => vm_try!(backiter.protocol_next()), + None => None, + }); + }; -/// Reduces the elements to a single one, by repeatedly applying a reducing -/// operation. -/// -/// If the iterator is empty, returns [`None`]; otherwise, returns the result of -/// the reduction. -/// -/// The reducing function is a closure with two arguments: an 'accumulator', and -/// an element. For iterators with at least one element, this is the same as -/// [`fold()`] with the first element of the iterator as the initial accumulator -/// value, folding every subsequent element into it. -/// -/// [`fold()`]: Iterator::fold -/// -/// # Example -/// -/// ```rune -/// let reduced = (1..10).iter().reduce(|acc, e| acc + e).unwrap(); -/// assert_eq!(reduced, 45); -/// -/// // Which is equivalent to doing it with `fold`: -/// let folded = (1..10).iter().fold(0, |acc, e| acc + e); -/// assert_eq!(reduced, folded); -/// ``` -#[rune::function(instance)] -#[inline] -fn reduce(this: Iterator, f: Function) -> VmResult> { - this.reduce(f) + self.frontiter = Some(vm_try!(value.protocol_into_iter())) + } + } + + #[rune::function(keep, protocol = NEXT_BACK)] + #[inline] + fn next_back(&mut self) -> VmResult> { + loop { + if let Some(ref mut iter) = self.backiter { + match vm_try!(iter.protocol_next_back()) { + None => self.backiter = None, + item @ Some(_) => return VmResult::Ok(item), + } + } + + let Some(value) = vm_try!(self.map.next_back()) else { + return VmResult::Ok(match &mut self.frontiter { + Some(frontiter) => vm_try!(frontiter.protocol_next_back()), + None => None, + }); + }; + + self.backiter = Some(vm_try!(value.protocol_into_iter())); + } + } + + #[rune::function(keep, protocol = SIZE_HINT)] + #[inline] + fn size_hint(&self) -> VmResult<(usize, Option)> { + let (flo, fhi) = match &self.frontiter { + Some(iter) => vm_try!(iter.protocol_size_hint()), + None => (0, Some(0)), + }; + + let (blo, bhi) = match &self.backiter { + Some(iter) => vm_try!(iter.protocol_size_hint()), + None => (0, Some(0)), + }; + + let lo = flo.saturating_add(blo); + + VmResult::Ok(match (vm_try!(self.map.size_hint()), fhi, bhi) { + ((0, Some(0)), Some(a), Some(b)) => (lo, a.checked_add(b)), + _ => (lo, None), + }) + } } -/// Reverses an iterator's direction. -/// -/// Usually, iterators iterate from left to right. After using `rev()`, an -/// iterator will instead iterate from right to left. -/// -/// This is only possible if the iterator has an end, so `rev()` only works on -/// double-ended iterators. -/// -/// # Examples -/// -/// ```rune -/// let a = [1, 2, 3]; -/// -/// let iter = a.iter().rev(); -/// -/// assert_eq!(iter.next(), Some(3)); -/// assert_eq!(iter.next(), Some(2)); -/// assert_eq!(iter.next(), Some(1)); -/// -/// assert_eq!(iter.next(), None); -/// ``` -#[rune::function(instance)] -#[inline] -fn rev(this: Iterator) -> VmResult { - this.rev() +#[derive(Any, Debug)] +#[rune(item = ::std::iter)] +struct Peekable { + iter: Value, + peeked: Option>, } -/// Returns the bounds on the remaining length of the iterator. -/// -/// Specifically, `size_hint()` returns a tuple where the first element -/// is the lower bound, and the second element is the upper bound. -/// -/// The second half of the tuple that is returned is an -/// [Option]<[i64]>. A [`None`] here means that either there is no -/// known upper bound, or the upper bound is larger than [`i64`]. -/// -/// # Implementation notes -/// -/// It is not enforced that an iterator implementation yields the declared -/// number of elements. A buggy iterator may yield less than the lower bound or -/// more than the upper bound of elements. -/// -/// `size_hint()` is primarily intended to be used for optimizations such as -/// reserving space for the elements of the iterator, but must not be trusted to -/// e.g., omit bounds checks in unsafe code. An incorrect implementation of -/// `size_hint()` should not lead to memory safety violations. -/// -/// That said, the implementation should provide a correct estimation, because -/// otherwise it would be a violation of the trait's protocol. -/// -/// The default implementation returns (0, [None]) which is correct -/// for any iterator. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let a = [1, 2, 3]; -/// let iter = a.iter(); -/// -/// assert_eq!((3, Some(3)), iter.size_hint()); -/// let _ = iter.next(); -/// assert_eq!((2, Some(2)), iter.size_hint()); -/// ``` -/// -/// A more complex example: -/// -/// ```rune -/// // The even numbers in the range of zero to nine. -/// let iter = (0..10).iter().filter(|x| x % 2 == 0); -/// -/// // We might iterate from zero to ten times. Knowing that it's five -/// // exactly wouldn't be possible without executing filter(). -/// assert_eq!((0, Some(10)), iter.size_hint()); -/// -/// // Let's add five more numbers with chain() -/// let iter = (0..10).iter().filter(|x| x % 2 == 0).chain(15..20); -/// -/// // now both bounds are increased by five -/// assert_eq!((5, Some(15)), iter.size_hint()); -/// ``` -/// -/// Returning `None` for an upper bound: -/// -/// ```rune -/// // an infinite iterator has no upper bound -/// // and the maximum possible lower bound -/// let iter = (0..).iter(); -/// -/// assert_eq!((i64::MAX, None), iter.size_hint()); -/// ``` -#[rune::function(instance)] -#[inline] -fn size_hint(this: &Iterator) -> (i64, Option) { - let (lower, upper) = this.size_hint(); - let lower = i64::try_from(lower).unwrap_or(i64::MAX); - let upper = upper.map(|upper| i64::try_from(upper).unwrap_or(i64::MAX)); - (lower, upper) +impl Peekable { + #[rune::function(keep, protocol = NEXT)] + #[inline] + fn next(&mut self) -> VmResult> { + VmResult::Ok(match self.peeked.take() { + Some(v) => v, + None => vm_try!(self.iter.protocol_next()), + }) + } + + #[rune::function(keep, protocol = NEXT_BACK)] + #[inline] + fn next_back(&mut self) -> VmResult> { + VmResult::Ok(match self.peeked.as_mut() { + Some(v @ Some(_)) => vm_try!(self.iter.protocol_next_back()).or_else(|| v.take()), + Some(None) => None, + None => vm_try!(self.iter.protocol_next_back()), + }) + } + + #[rune::function(keep, protocol = SIZE_HINT)] + #[inline] + fn size_hint(&self) -> VmResult<(usize, Option)> { + let peek_len = match &self.peeked { + Some(None) => return VmResult::Ok((0, Some(0))), + Some(Some(_)) => 1, + None => 0, + }; + + let (lo, hi) = vm_try!(self.iter.protocol_size_hint()); + let lo = lo.saturating_add(peek_len); + + let hi = match hi { + Some(x) => x.checked_add(peek_len), + None => None, + }; + + VmResult::Ok((lo, hi)) + } + + #[rune::function(keep, protocol = LEN)] + #[inline] + fn len(&self) -> VmResult { + let peek_len = match &self.peeked { + Some(None) => return VmResult::Ok(0), + Some(Some(_)) => 1, + None => 0, + }; + + let len = vm_try!(self.iter.protocol_len()); + VmResult::Ok(len.saturating_add(peek_len)) + } + + /// Returns a reference to the `next()` value without advancing the iterator. + /// + /// Like [`next`], if there is a value, it is wrapped in a `Some(T)`. But if the + /// iteration is over, `None` is returned. + /// + /// [`next`]: Iterator::next + /// + /// Because `peek()` returns a reference, and many iterators iterate over + /// references, there can be a possibly confusing situation where the return + /// value is a double reference. You can see this effect in the examples below. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rune + /// let xs = [1, 2, 3]; + /// + /// let iter = xs.iter().peekable(); + /// + /// // peek() lets us see into the future + /// assert_eq!(iter.peek(), Some(1)); + /// assert_eq!(iter.next(), Some(1)); + /// + /// assert_eq!(iter.next(), Some(2)); + /// + /// // The iterator does not advance even if we `peek` multiple times + /// assert_eq!(iter.peek(), Some(3)); + /// assert_eq!(iter.peek(), Some(3)); + /// + /// assert_eq!(iter.next(), Some(3)); + /// + /// // After the iterator is finished, so is `peek()` + /// assert_eq!(iter.peek(), None); + /// assert_eq!(iter.next(), None); + /// ``` + #[rune::function(keep)] + #[inline] + fn peek(&mut self) -> VmResult> { + if let Some(v) = &self.peeked { + return VmResult::Ok(v.clone()); + } + + let value = vm_try!(self.iter.protocol_next()); + self.peeked = Some(value.clone()); + VmResult::Ok(value) + } } -/// Creates an iterator that skips the first `n` elements. -/// -/// `skip(n)` skips elements until `n` elements are skipped or the end of the -/// iterator is reached (whichever happens first). After that, all the remaining -/// elements are yielded. In particular, if the original iterator is too short, -/// then the returned iterator is empty. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let a = [1, 2, 3]; -/// -/// let iter = a.iter().skip(2); -/// -/// assert_eq!(iter.next(), Some(3)); -/// assert_eq!(iter.next(), None); -/// ``` -#[rune::function(instance)] -#[inline] -fn skip(this: Iterator, n: usize) -> Iterator { - this.skip(n) +#[derive(Any, Debug)] +#[rune(item = ::std::iter)] +struct Skip { + iter: Value, + n: usize, } -/// Creates an iterator that yields the first `n` elements, or fewer if the -/// underlying iterator ends sooner. -/// -/// `take(n)` yields elements until `n` elements are yielded or the end of the -/// iterator is reached (whichever happens first). The returned iterator is a -/// prefix of length `n` if the original iterator contains at least `n` -/// elements, otherwise it contains all of the (fewer than `n`) elements of the -/// original iterator. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let a = [1, 2, 3]; -/// -/// let iter = a.iter().take(2); -/// -/// assert_eq!(iter.next(), Some(1)); -/// assert_eq!(iter.next(), Some(2)); -/// assert_eq!(iter.next(), None); -/// ``` -/// -/// `take()` is often used with an infinite iterator, to make it finite: -/// -/// ```rune -/// let iter = (0..).iter().take(3); -/// -/// assert_eq!(iter.next(), Some(0)); -/// assert_eq!(iter.next(), Some(1)); -/// assert_eq!(iter.next(), Some(2)); -/// assert_eq!(iter.next(), None); -/// ``` -/// -/// If less than `n` elements are available, `take` will limit itself to the -/// size of the underlying iterator: -/// -/// ```rune -/// let v = [1, 2]; -/// let iter = v.iter().take(5); -/// assert_eq!(iter.next(), Some(1)); -/// assert_eq!(iter.next(), Some(2)); -/// assert_eq!(iter.next(), None); -/// ``` -#[rune::function(instance)] -#[inline] -fn take(this: Iterator, n: usize) -> Iterator { - this.take(n) +impl Skip { + #[rune::function(keep, protocol = NEXT)] + #[inline] + fn next(&mut self) -> VmResult> { + if self.n > 0 { + let old_n = self.n; + self.n = 0; + + for _ in 0..old_n { + match vm_try!(self.iter.protocol_next()) { + Some(..) => (), + None => return VmResult::Ok(None), + } + } + } + + self.iter.protocol_next() + } + + #[rune::function(keep, protocol = NEXT_BACK)] + #[inline] + fn next_back(&mut self) -> VmResult> { + VmResult::Ok(if vm_try!(self.len()) > 0 { + vm_try!(self.iter.protocol_next_back()) + } else { + None + }) + } + + #[rune::function(keep, protocol = SIZE_HINT)] + #[inline] + fn size_hint(&self) -> VmResult<(usize, Option)> { + let (lower, upper) = vm_try!(self.iter.protocol_size_hint()); + let lower = lower.saturating_sub(self.n); + let upper = upper.map(|x| x.saturating_sub(self.n)); + VmResult::Ok((lower, upper)) + } + + #[rune::function(keep, protocol = LEN)] + #[inline] + fn len(&self) -> VmResult { + let len = vm_try!(self.iter.protocol_len()); + VmResult::Ok(len.saturating_sub(self.n)) + } } -/// Consumes the iterator, counting the number of iterations and returning it. -/// -/// This method will call [`next`] repeatedly until [`None`] is encountered, -/// returning the number of times it saw [`Some`]. Note that [`next`] has to be -/// called at least once even if the iterator does not have any elements. -/// -/// [`next`]: Iterator::next -/// -/// # Overflow Behavior -/// -/// The method does no guarding against overflows, so counting elements of an -/// iterator with more than [`i64::MAX`] elements panics. -/// -/// # Panics -/// -/// This function might panic if the iterator has more than [`i64::MAX`] -/// elements. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ```rune -/// let a = [1, 2, 3]; -/// assert_eq!(a.iter().count(), 3); -/// -/// let a = [1, 2, 3, 4, 5]; -/// assert_eq!(a.iter().count(), 5); -/// ``` -#[rune::function(instance)] -#[inline] -fn count(this: &mut Iterator) -> VmResult { - this.count() +#[derive(Any, Debug)] +#[rune(item = ::std::iter)] +struct Take { + iter: Value, + n: usize, } -/// Collect the iterator as a [`Vec`]. -/// -/// # Examples -/// -/// ```rune -/// use std::iter::range; -/// -/// assert_eq!((0..3).iter().collect::(), [0, 1, 2]); -/// ``` -#[rune::function(instance, path = collect::)] -fn collect_vec(it: Iterator) -> VmResult { - VmResult::Ok(Vec::from(vm_try!(it.collect::()))) +impl Take { + #[rune::function(keep, protocol = NEXT)] + #[inline] + fn next(&mut self) -> VmResult> { + if self.n == 0 { + return VmResult::Ok(None); + } + + self.n -= 1; + self.iter.protocol_next() + } + + #[rune::function(keep, protocol = NEXT_BACK)] + #[inline] + fn next_back(&mut self) -> VmResult> { + if self.n == 0 { + VmResult::Ok(None) + } else { + let n = self.n; + self.n -= 1; + let len = vm_try!(self.iter.protocol_len()); + self.iter.protocol_nth_back(len.saturating_sub(n)) + } + } + + #[rune::function(keep, protocol = SIZE_HINT)] + #[inline] + fn size_hint(&self) -> VmResult<(usize, Option)> { + if self.n == 0 { + return VmResult::Ok((0, Some(0))); + } + + let (lower, upper) = vm_try!(self.iter.protocol_size_hint()); + + let lower = lower.min(self.n); + + let upper = match upper { + Some(x) if x < self.n => Some(x), + _ => Some(self.n), + }; + + VmResult::Ok((lower, upper)) + } + + #[rune::function(keep, protocol = LEN)] + #[inline] + fn len(&self) -> VmResult { + if self.n == 0 { + return VmResult::Ok(0); + } + + let len = vm_try!(self.iter.protocol_len()); + VmResult::Ok(len.min(self.n)) + } } -/// Collect the iterator as a [`VecDeque`]. -/// -/// # Examples -/// -/// ```rune -/// use std::collections::VecDeque; -/// -/// assert_eq!((0..3).iter().collect::(), VecDeque::from([0, 1, 2])); -/// ``` -#[rune::function(instance, path = collect::)] -fn collect_vec_deque(it: Iterator) -> VmResult { - VecDeque::from_iter(it) +#[derive(Any, Debug)] +#[rune(item = ::std::iter)] +struct Rev { + value: Value, } -/// Collect the iterator as a [`HashSet`]. -/// -/// # Examples -/// -/// ```rune -/// use std::collections::HashSet; -/// -/// assert_eq!((0..3).iter().collect::(), HashSet::from([0, 1, 2])); -/// ``` -#[rune::function(instance, path = collect::)] -fn collect_hash_set(it: Iterator) -> VmResult { - let mut caller = EnvProtocolCaller; - HashSet::from_iter(it, &mut caller) +impl Rev { + #[rune::function(keep, protocol = NEXT)] + #[inline] + fn next(&mut self) -> VmResult> { + self.value.protocol_next_back() + } + + #[rune::function(keep, protocol = NEXT_BACK)] + #[inline] + fn next_back(&mut self) -> VmResult> { + self.value.protocol_next() + } + + #[rune::function(keep, protocol = SIZE_HINT)] + #[inline] + fn size_hint(&self) -> VmResult<(usize, Option)> { + self.value.protocol_size_hint() + } + + #[rune::function(keep, protocol = LEN)] + #[inline] + fn len(&self) -> VmResult { + self.value.protocol_len() + } } -/// Collect the iterator as a [`HashMap`]. -/// -/// # Examples -/// -/// ```rune -/// use std::collections::HashMap; -/// -/// let actual = (0..3).iter().map(|n| (n, n.to_string())).collect::(); -/// let expected = HashMap::from([(0, "0"), (1, "1"), (2, "2")]); -/// assert_eq!(actual, expected); -/// ``` -#[rune::function(instance, path = collect::)] -fn collect_hash_map(it: Iterator) -> VmResult { - let mut caller = EnvProtocolCaller; - HashMap::from_iter(it, &mut caller) +pub(crate) trait CheckedOps: Sized { + const ONE: Self; + const ZERO: Self; + + fn checked_add(self, value: Self) -> Option; + fn checked_mul(self, value: Self) -> Option; } -/// Collect the iterator as a [`Tuple`]. -/// -/// # Examples -/// -/// ```rune -/// assert_eq!((0..3).iter().collect::(), (0, 1, 2)); -/// ``` -#[rune::function(instance, path = collect::)] -fn collect_tuple(it: Iterator) -> VmResult { - VmResult::Ok(vm_try!(OwnedTuple::try_from( - vm_try!(it.collect::()) - ))) +impl CheckedOps for u8 { + const ONE: Self = 1; + const ZERO: Self = 0; + + #[inline] + fn checked_add(self, value: Self) -> Option { + u8::checked_add(self, value) + } + + #[inline] + fn checked_mul(self, value: Self) -> Option { + u8::checked_mul(self, value) + } } -/// Collect the iterator as an [`Object`]. -/// -/// # Examples -/// -/// ```rune -/// assert_eq!([("first", 1), ("second", 2)].iter().collect::(), #{first: 1, second: 2}); -/// ``` -#[rune::function(instance, path = collect::)] -fn collect_object(mut it: Iterator) -> VmResult { - let (cap, _) = it.size_hint(); - let mut object = vm_try!(Object::with_capacity(cap)); - - while let Some(value) = vm_try!(it.next()) { - let (key, value) = vm_try!(<(String, Value)>::from_value(value)); - vm_try!(object.insert(key, value)); +impl CheckedOps for i64 { + const ONE: Self = 1; + const ZERO: Self = 0; + + #[inline] + fn checked_add(self, value: Self) -> Option { + i64::checked_add(self, value) } - VmResult::Ok(object) + #[inline] + fn checked_mul(self, value: Self) -> Option { + i64::checked_mul(self, value) + } } -/// Collect the iterator as a [`String`]. -/// -/// # Examples -/// -/// ```rune -/// assert_eq!(["first", "second"].iter().collect::(), "firstsecond"); -/// ``` -#[rune::function(instance, path = collect::)] -fn collect_string(mut it: Iterator) -> VmResult { - let mut string = String::new(); - - while let Some(value) = vm_try!(it.next()) { - match &*vm_try!(value.borrow_kind_ref()) { - ValueKind::Char(c) => { - vm_try!(string.try_push(*c)); - } - ValueKind::String(s) => { - vm_try!(string.try_push_str(s)); - } - value => { - return VmResult::expected::(value.type_info()); - } - } +impl CheckedOps for f64 { + const ONE: Self = 1.0; + const ZERO: Self = 0.0; + + #[inline] + fn checked_add(self, value: Self) -> Option { + Some(self + value) } - VmResult::Ok(string) + #[inline] + fn checked_mul(self, value: Self) -> Option { + Some(self * value) + } } diff --git a/crates/rune/src/modules/mod.rs b/crates/rune/src/modules/mod.rs index ff3fdc5df..4dc6d917c 100644 --- a/crates/rune/src/modules/mod.rs +++ b/crates/rune/src/modules/mod.rs @@ -4,6 +4,13 @@ //! These are usually included through //! [`Context::with_default_modules`][crate::Context::with_default_modules]. +// Note: A fair amount of code and documentation in this and child modules is +// duplicated from the Rust project under the MIT license. +// +// https://github.com/rust-lang/rust +// +// Copyright 2014-2024 The Rust Project Developers + pub mod any; pub mod bytes; #[cfg(feature = "capture-io")] @@ -30,6 +37,7 @@ pub mod object; pub mod ops; pub mod option; pub mod result; +pub mod slice; pub mod stream; pub mod string; pub mod test; diff --git a/crates/rune/src/modules/object.rs b/crates/rune/src/modules/object.rs index 82d4d9c7b..dcb9c0d4c 100644 --- a/crates/rune/src/modules/object.rs +++ b/crates/rune/src/modules/object.rs @@ -3,8 +3,8 @@ use core::cmp::Ordering; use crate as rune; -use crate::alloc::prelude::*; -use crate::runtime::{EnvProtocolCaller, Iterator, Object, Protocol, Value, VmResult}; +use crate::runtime::object::{RuneIter, RuneIterKeys, RuneValues}; +use crate::runtime::{EnvProtocolCaller, Object, Protocol, Value, VmResult}; use crate::{ContextError, Module}; /// The dynamic [`Object`] container. @@ -50,13 +50,42 @@ pub fn module() -> Result { m.function_meta(get)?; m.function_meta(Object::rune_iter__meta)?; - m.function_meta(keys)?; - m.function_meta(values)?; + m.function_meta(Object::rune_keys__meta)?; + m.function_meta(Object::rune_values__meta)?; m.associated_function(Protocol::INTO_ITER, Object::rune_iter)?; - m.function_meta(partial_eq)?; - m.function_meta(eq)?; - m.function_meta(partial_cmp)?; - m.function_meta(cmp)?; + + m.function_meta(partial_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + + m.function_meta(eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + + m.function_meta(partial_cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialOrd))?; + + m.function_meta(cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Ord))?; + + m.ty::()?; + m.function_meta(RuneIter::next__meta)?; + m.function_meta(RuneIter::size_hint__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.function_meta(RuneIter::len__meta)?; + m.implement_trait::(rune::item!(::std::iter::ExactSizeIterator))?; + + m.ty::()?; + m.function_meta(RuneIterKeys::next__meta)?; + m.function_meta(RuneIterKeys::size_hint__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.function_meta(RuneIterKeys::len__meta)?; + m.implement_trait::(rune::item!(::std::iter::ExactSizeIterator))?; + + m.ty::()?; + m.function_meta(RuneValues::next__meta)?; + m.function_meta(RuneValues::size_hint__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.function_meta(RuneValues::len__meta)?; + m.implement_trait::(rune::item!(::std::iter::ExactSizeIterator))?; Ok(m) } @@ -105,78 +134,22 @@ fn get(object: &Object, key: &str) -> Option { object.get(key).cloned() } -/// An iterator visiting all keys in arbitrary order. -/// -/// # Examples -/// -/// ```rune -/// let object = #{a: 1, b: 2, c: 3}; -/// let vec = []; -/// -/// for key in object.keys() { -/// vec.push(key); -/// } -/// -/// vec.sort(); -/// assert_eq!(vec, ["a", "b", "c"]); -/// ``` -#[inline] -#[rune::function(vm_result, instance)] -fn keys(object: &Object) -> Iterator { - // TODO: implement as lazy iteration. - let mut keys = Vec::new(); - - for key in object.keys() { - keys.try_push(key.try_clone().vm?).vm?; - } - - Iterator::from_double_ended("std::object::Keys", keys.into_iter()) -} - -/// An iterator visiting all values in arbitrary order. -/// -/// # Examples -/// -/// ```rune -/// let object = #{a: 1, b: 2, c: 3}; -/// let vec = []; -/// -/// for key in object.values() { -/// vec.push(key); -/// } -/// -/// vec.sort(); -/// assert_eq!(vec, [1, 2, 3]); -/// ``` -#[inline] -#[rune::function(vm_result, instance)] -fn values(object: &Object) -> Iterator { - // TODO: implement as lazy iteration. - let iter = object - .values() - .cloned() - .try_collect::>() - .vm? - .into_iter(); - Iterator::from_double_ended("std::object::Values", iter) -} - -#[rune::function(instance, protocol = PARTIAL_EQ)] +#[rune::function(keep, instance, protocol = PARTIAL_EQ)] fn partial_eq(this: &Object, other: Value) -> VmResult { Object::partial_eq_with(this, other, &mut EnvProtocolCaller) } -#[rune::function(instance, protocol = EQ)] +#[rune::function(keep, instance, protocol = EQ)] fn eq(this: &Object, other: &Object) -> VmResult { Object::eq_with(this, other, Value::eq_with, &mut EnvProtocolCaller) } -#[rune::function(instance, protocol = PARTIAL_CMP)] +#[rune::function(keep, instance, protocol = PARTIAL_CMP)] fn partial_cmp(this: &Object, other: &Object) -> VmResult> { Object::partial_cmp_with(this, other, &mut EnvProtocolCaller) } -#[rune::function(instance, protocol = CMP)] +#[rune::function(keep, instance, protocol = CMP)] fn cmp(this: &Object, other: &Object) -> VmResult { Object::cmp_with(this, other, &mut EnvProtocolCaller) } diff --git a/crates/rune/src/modules/ops.rs b/crates/rune/src/modules/ops.rs index 817f243d4..c24b12618 100644 --- a/crates/rune/src/modules/ops.rs +++ b/crates/rune/src/modules/ops.rs @@ -1,14 +1,19 @@ //! Overloadable operators and associated types. +pub mod generator; + use core::cmp::Ordering; use once_cell::sync::OnceCell; use rune_alloc::hash_map::RandomState; use crate as rune; +use crate::runtime::range::RangeIter; +use crate::runtime::range_from::RangeFromIter; +use crate::runtime::range_inclusive::RangeInclusiveIter; use crate::runtime::{ - ControlFlow, EnvProtocolCaller, Function, Generator, GeneratorState, Hasher, Iterator, Range, - RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, Value, Vm, VmResult, + ControlFlow, EnvProtocolCaller, Function, Hasher, Range, RangeFrom, RangeFull, RangeInclusive, + RangeTo, RangeToInclusive, Value, VmResult, }; use crate::{ContextError, Module}; @@ -19,15 +24,64 @@ static STATE: OnceCell = OnceCell::new(); pub fn module() -> Result { let mut m = Module::from_meta(self::module_meta)?; + macro_rules! iter { + ($ty:ident) => { + m.ty::<$ty>()?; + m.function_meta($ty::::next__meta)?; + m.function_meta($ty::::size_hint__meta)?; + m.implement_trait::<$ty>(rune::item!(::std::iter::Iterator))?; + + m.ty::<$ty>()?; + m.function_meta($ty::::next__meta)?; + m.function_meta($ty::::size_hint__meta)?; + m.implement_trait::<$ty>(rune::item!(::std::iter::Iterator))?; + + m.ty::<$ty>()?; + m.function_meta($ty::::next__meta)?; + m.function_meta($ty::::size_hint__meta)?; + m.implement_trait::<$ty>(rune::item!(::std::iter::Iterator))?; + }; + } + + macro_rules! double_ended { + ($ty:ident) => { + iter!($ty); + m.function_meta($ty::::next_back__meta)?; + m.implement_trait::<$ty>(rune::item!(::std::iter::DoubleEndedIterator))?; + + m.function_meta($ty::::len__meta)?; + m.implement_trait::<$ty>(rune::item!(::std::iter::ExactSizeIterator))?; + + m.function_meta($ty::::next_back__meta)?; + m.implement_trait::<$ty>(rune::item!(::std::iter::DoubleEndedIterator))?; + + m.function_meta($ty::::len__meta)?; + m.implement_trait::<$ty>(rune::item!(::std::iter::ExactSizeIterator))?; + + m.function_meta($ty::::next_back__meta)?; + m.implement_trait::<$ty>(rune::item!(::std::iter::DoubleEndedIterator))?; + }; + } + { m.ty::()?; m.function_meta(RangeFrom::iter__meta)?; - m.function_meta(RangeFrom::contains__meta)?; m.function_meta(RangeFrom::into_iter__meta)?; + m.function_meta(RangeFrom::contains__meta)?; + m.function_meta(RangeFrom::partial_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + m.function_meta(RangeFrom::eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + m.function_meta(RangeFrom::partial_cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialOrd))?; + m.function_meta(RangeFrom::cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Ord))?; + + iter!(RangeFromIter); } { @@ -38,30 +92,56 @@ pub fn module() -> Result { { m.ty::()?; m.function_meta(RangeInclusive::iter__meta)?; - m.function_meta(RangeInclusive::contains__meta)?; m.function_meta(RangeInclusive::into_iter__meta)?; + m.function_meta(RangeInclusive::contains__meta)?; + m.function_meta(RangeInclusive::partial_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + m.function_meta(RangeInclusive::eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + m.function_meta(RangeInclusive::partial_cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialOrd))?; + m.function_meta(RangeInclusive::cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Ord))?; + + double_ended!(RangeInclusiveIter); } { m.ty::()?; m.function_meta(RangeToInclusive::contains__meta)?; + m.function_meta(RangeToInclusive::partial_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + m.function_meta(RangeToInclusive::eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + m.function_meta(RangeToInclusive::partial_cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialOrd))?; + m.function_meta(RangeToInclusive::cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Ord))?; } { m.ty::()?; m.function_meta(RangeTo::contains__meta)?; + m.function_meta(RangeTo::partial_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + m.function_meta(RangeTo::eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + m.function_meta(RangeTo::partial_cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialOrd))?; + m.function_meta(RangeTo::cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Ord))?; } { @@ -69,10 +149,20 @@ pub fn module() -> Result { m.function_meta(Range::iter__meta)?; m.function_meta(Range::into_iter__meta)?; m.function_meta(Range::contains__meta)?; + m.function_meta(Range::partial_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + m.function_meta(Range::eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + m.function_meta(Range::partial_cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialOrd))?; + m.function_meta(Range::cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Ord))?; + + double_ended!(RangeIter); } { @@ -81,27 +171,17 @@ pub fn module() -> Result { m.ty::()?; - { - m.ty::>()?; - m.function_meta(generator_next)?; - m.function_meta(generator_resume)?; - m.function_meta(generator_iter)?; - m.function_meta(generator_into_iter)?; - } - - { - m.generator_state(["GeneratorState"])? - .docs(["Enum indicating the state of a generator."])?; - - m.function_meta(generator_state_partial_eq)?; - m.function_meta(generator_state_eq)?; - } - m.function_meta(partial_eq)?; m.function_meta(eq)?; m.function_meta(partial_cmp)?; m.function_meta(cmp)?; m.function_meta(hash)?; + + m.reexport(["Generator"], rune::item!(::std::ops::generator::Generator))?; + m.reexport( + ["GeneratorState"], + rune::item!(::std::ops::generator::GeneratorState), + )?; Ok(m) } @@ -242,108 +322,3 @@ fn hash(value: Value) -> VmResult { VmResult::Ok(hasher.finish() as i64) } - -/// Advance a generator producing the next value yielded. -/// -/// Unlike [`Generator::resume`], this can only consume the yielded values. -/// -/// # Examples -/// -/// ```rune -/// use std::ops::{Generator, GeneratorState}; -/// -/// fn generate() { -/// yield 1; -/// yield 2; -/// } -/// -/// let g = generate(); -/// -/// assert_eq!(g.next(), Some(1)); -/// assert_eq!(g.next(), Some(2)); -/// assert_eq!(g.next(), None); -/// `` -#[rune::function(instance, path = next)] -fn generator_next(this: &mut Generator) -> VmResult> { - this.next() -} - -/// Advance a generator producing the next [`GeneratorState`]. -/// -/// # Examples -/// -/// ```rune -/// use std::ops::{Generator, GeneratorState}; -/// -/// fn generate() { -/// let n = yield 1; -/// yield 2 + n; -/// } -/// -/// let g = generate(); -/// -/// assert_eq!(g.resume(()), GeneratorState::Yielded(1)); -/// assert_eq!(g.resume(1), GeneratorState::Yielded(3)); -/// assert_eq!(g.resume(()), GeneratorState::Complete(())); -/// `` -#[rune::function(instance, path = resume)] -fn generator_resume(this: &mut Generator, value: Value) -> VmResult { - this.resume(value) -} - -#[rune::function(instance, path = iter)] -fn generator_iter(this: Generator) -> Iterator { - this.rune_iter() -} - -#[rune::function(instance, protocol = INTO_ITER)] -fn generator_into_iter(this: Generator) -> Iterator { - this.rune_iter() -} - -/// Test for partial equality over a generator state. -/// -/// # Examples -/// -/// ```rune -/// use std::ops::{Generator, GeneratorState}; -/// -/// fn generate() { -/// let n = yield 1; -/// yield 2 + n; -/// } -/// -/// let g = generate(); -/// -/// assert_eq!(g.resume(()), GeneratorState::Yielded(1)); -/// assert_eq!(g.resume(1), GeneratorState::Yielded(3)); -/// assert_eq!(g.resume(()), GeneratorState::Complete(())); -/// `` -#[rune::function(instance, protocol = PARTIAL_EQ)] -fn generator_state_partial_eq(this: &GeneratorState, other: &GeneratorState) -> VmResult { - this.partial_eq_with(other, &mut EnvProtocolCaller) -} - -/// Test for total equality over a generator state. -/// -/// # Examples -/// -/// ```rune -/// use std::ops::{Generator, GeneratorState}; -/// use std::ops::eq; -/// -/// fn generate() { -/// let n = yield 1; -/// yield 2 + n; -/// } -/// -/// let g = generate(); -/// -/// assert!(eq(g.resume(()), GeneratorState::Yielded(1))); -/// assert!(eq(g.resume(1), GeneratorState::Yielded(3))); -/// assert!(eq(g.resume(()), GeneratorState::Complete(()))); -/// `` -#[rune::function(instance, protocol = EQ)] -fn generator_state_eq(this: &GeneratorState, other: &GeneratorState) -> VmResult { - this.eq_with(other, &mut EnvProtocolCaller) -} diff --git a/crates/rune/src/modules/ops/generator.rs b/crates/rune/src/modules/ops/generator.rs new file mode 100644 index 000000000..110dc1252 --- /dev/null +++ b/crates/rune/src/modules/ops/generator.rs @@ -0,0 +1,143 @@ +//! Overloadable operators and associated types. + +use crate as rune; +use crate::runtime::generator::Iter; +use crate::runtime::{EnvProtocolCaller, Generator, GeneratorState, Value, Vm, VmResult}; +use crate::{ContextError, Module}; + +/// Types related to generators. +#[rune::module(::std::ops::generator)] +pub fn module() -> Result { + let mut m = Module::from_meta(self::module_meta)?; + + { + m.ty::>()?; + m.function_meta(generator_next)?; + m.function_meta(generator_resume)?; + m.function_meta(generator_iter)?; + m.function_meta(generator_into_iter)?; + + m.ty::()?; + m.function_meta(Iter::next__meta)?; + } + + { + m.generator_state(["GeneratorState"])? + .docs(["Enum indicating the state of a generator."])?; + + m.function_meta(generator_state_partial_eq)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + + m.function_meta(generator_state_eq)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + } + + Ok(m) +} + +/// Advance a generator producing the next value yielded. +/// +/// Unlike [`Generator::resume`], this can only consume the yielded values. +/// +/// # Examples +/// +/// ```rune +/// use std::ops::{Generator, GeneratorState}; +/// +/// fn generate() { +/// yield 1; +/// yield 2; +/// } +/// +/// let g = generate(); +/// +/// assert_eq!(g.next(), Some(1)); +/// assert_eq!(g.next(), Some(2)); +/// assert_eq!(g.next(), None); +/// `` +#[rune::function(instance, path = next)] +fn generator_next(this: &mut Generator) -> VmResult> { + this.next() +} + +/// Advance a generator producing the next [`GeneratorState`]. +/// +/// # Examples +/// +/// ```rune +/// use std::ops::{Generator, GeneratorState}; +/// +/// fn generate() { +/// let n = yield 1; +/// yield 2 + n; +/// } +/// +/// let g = generate(); +/// +/// assert_eq!(g.resume(()), GeneratorState::Yielded(1)); +/// assert_eq!(g.resume(1), GeneratorState::Yielded(3)); +/// assert_eq!(g.resume(()), GeneratorState::Complete(())); +/// `` +#[rune::function(instance, path = resume)] +fn generator_resume(this: &mut Generator, value: Value) -> VmResult { + this.resume(value) +} + +#[rune::function(instance, path = iter)] +#[inline] +fn generator_iter(this: Generator) -> Iter { + this.rune_iter() +} + +#[rune::function(instance, protocol = INTO_ITER)] +#[inline] +fn generator_into_iter(this: Generator) -> Iter { + this.rune_iter() +} + +/// Test for partial equality over a generator state. +/// +/// # Examples +/// +/// ```rune +/// use std::ops::{Generator, GeneratorState}; +/// +/// fn generate() { +/// let n = yield 1; +/// yield 2 + n; +/// } +/// +/// let g = generate(); +/// +/// assert_eq!(g.resume(()), GeneratorState::Yielded(1)); +/// assert_eq!(g.resume(1), GeneratorState::Yielded(3)); +/// assert_eq!(g.resume(()), GeneratorState::Complete(())); +/// `` +#[rune::function(instance, protocol = PARTIAL_EQ)] +fn generator_state_partial_eq(this: &GeneratorState, other: &GeneratorState) -> VmResult { + this.partial_eq_with(other, &mut EnvProtocolCaller) +} + +/// Test for total equality over a generator state. +/// +/// # Examples +/// +/// ```rune +/// use std::ops::{Generator, GeneratorState}; +/// use std::ops::eq; +/// +/// fn generate() { +/// let n = yield 1; +/// yield 2 + n; +/// } +/// +/// let g = generate(); +/// +/// assert!(eq(g.resume(()), GeneratorState::Yielded(1))); +/// assert!(eq(g.resume(1), GeneratorState::Yielded(3))); +/// assert!(eq(g.resume(()), GeneratorState::Complete(()))); +/// `` +#[rune::function(instance, protocol = EQ)] +fn generator_state_eq(this: &GeneratorState, other: &GeneratorState) -> VmResult { + this.eq_with(other, &mut EnvProtocolCaller) +} diff --git a/crates/rune/src/modules/option.rs b/crates/rune/src/modules/option.rs index 1e6d9e086..528b9254a 100644 --- a/crates/rune/src/modules/option.rs +++ b/crates/rune/src/modules/option.rs @@ -1,7 +1,8 @@ //! The [`Option`] type. use crate as rune; -use crate::runtime::{ControlFlow, Formatter, Function, Iterator, Panic, Value, VmResult}; +use crate::runtime::{ControlFlow, Formatter, Function, Panic, Value, VmResult}; +use crate::Any; use crate::{ContextError, Module}; /// The [`Option`] type. @@ -9,26 +10,34 @@ use crate::{ContextError, Module}; /// This module deals with the fundamental [`Option`] type in rune. #[rune::module(::std::option)] pub fn module() -> Result { - let mut module = Module::from_meta(self::module_meta)?; - module.option(["Option"])?; + let mut m = Module::from_meta(self::module_meta)?; + m.option(["Option"])?; // Sorted for ease of finding - module.function_meta(expect)?; - module.function_meta(unwrap)?; - module.function_meta(unwrap_or)?; - module.function_meta(unwrap_or_else)?; - module.function_meta(is_some)?; - module.function_meta(is_none)?; - module.function_meta(iter)?; - module.function_meta(and_then)?; - module.function_meta(map)?; - module.function_meta(take)?; - module.function_meta(transpose)?; - module.function_meta(ok_or)?; - module.function_meta(ok_or_else)?; - module.function_meta(into_iter)?; - module.function_meta(option_try__meta)?; - Ok(module) + m.function_meta(expect)?; + m.function_meta(unwrap)?; + m.function_meta(unwrap_or)?; + m.function_meta(unwrap_or_else)?; + m.function_meta(is_some)?; + m.function_meta(is_none)?; + m.function_meta(iter)?; + m.function_meta(and_then)?; + m.function_meta(map)?; + m.function_meta(take)?; + m.function_meta(transpose)?; + m.function_meta(ok_or)?; + m.function_meta(ok_or_else)?; + m.function_meta(into_iter)?; + m.function_meta(option_try__meta)?; + + m.ty::()?; + m.function_meta(Iter::next__meta)?; + m.function_meta(Iter::next_back__meta)?; + m.function_meta(Iter::size_hint__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.implement_trait::(rune::item!(::std::iter::DoubleEndedIterator))?; + + Ok(m) } /// Returns the contained [`Some`] value, consuming the `self` value. @@ -130,8 +139,8 @@ fn is_none(this: &Option) -> bool { /// assert_eq!(None, it.next()); /// ``` #[rune::function(instance)] -fn iter(option: Option) -> Iterator { - Iterator::from_double_ended("std::option::Iter", option.into_iter()) +fn iter(value: Option) -> Iter { + Iter { value } } /// Construct an iterator over an optional value. @@ -150,8 +159,8 @@ fn iter(option: Option) -> Iterator { /// assert_eq!(out, [1]); /// ``` #[rune::function(instance, protocol = INTO_ITER)] -fn into_iter(option: Option) -> Iterator { - __rune_fn__iter(option) +fn into_iter(this: Option) -> Iter { + Iter::new(this) } /// Returns [`None`] if the option is [`None`], otherwise calls `f` with the @@ -414,3 +423,41 @@ pub(crate) fn option_try(this: &Option) -> VmResult { None => ControlFlow::Break(vm_try!(Value::try_from(None))), }) } + +#[derive(Any)] +#[rune(item = ::std::option)] +pub(crate) struct Iter { + value: Option, +} + +impl Iter { + /// Construct a new iterator. + fn new(value: Option) -> Self { + Self { value } + } + + /// Get the next value in the iterator. + #[rune::function(keep, protocol = NEXT)] + fn next(&mut self) -> Option { + self.value.take() + } + + /// Get the next back value in the iterator. + #[rune::function(keep, protocol = NEXT_BACK)] + fn next_back(&mut self) -> Option { + self.value.take() + } + + /// Get the size hint. + #[rune::function(keep, protocol = SIZE_HINT)] + fn size_hint(&self) -> (usize, Option) { + let len = usize::from(self.value.is_some()); + (len, Some(len)) + } + + /// Convert into an iterator. + #[rune::function(keep, protocol = INTO_ITER)] + fn into_iter(self) -> Self { + self + } +} diff --git a/crates/rune/src/modules/slice.rs b/crates/rune/src/modules/slice.rs new file mode 100644 index 000000000..37ab3a9cd --- /dev/null +++ b/crates/rune/src/modules/slice.rs @@ -0,0 +1,32 @@ +//! Types relates to working with slices. + +use crate as rune; +use crate::runtime::slice::Iter; +use crate::{ContextError, Module}; + +/// Types related to working with contiguous slices. +/// +/// # Examples +/// +/// ```rune +/// let it = [10, 20].iter(); +/// +/// assert_eq!(it.next(), Some(10)); +/// assert_eq!(it.next(), Some(20)); +/// assert_eq!(it.next(), None); +/// ``` +#[rune::module(::std::slice)] +pub fn module() -> Result { + let mut m = Module::from_meta(self::module_meta)?; + + m.ty::()?; + m.function_meta(Iter::next__meta)?; + m.function_meta(Iter::size_hint__meta)?; + m.function_meta(Iter::len__meta)?; + m.function_meta(Iter::nth__meta)?; + m.function_meta(Iter::next_back__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.implement_trait::(rune::item!(::std::iter::DoubleEndedIterator))?; + + Ok(m) +} diff --git a/crates/rune/src/modules/string.rs b/crates/rune/src/modules/string.rs index ad4d86170..1ab3c0964 100644 --- a/crates/rune/src/modules/string.rs +++ b/crates/rune/src/modules/string.rs @@ -10,7 +10,11 @@ use crate::alloc::fmt::TryWrite; use crate::alloc::prelude::*; use crate::alloc::string::FromUtf8Error; use crate::alloc::{String, Vec}; -use crate::runtime::{Bytes, Formatter, Iterator, Panic, Value, ValueKind, VmErrorKind, VmResult}; +use crate::compile::Named; +use crate::runtime::{ + Bytes, Formatter, FromValue, Function, MaybeTypeOf, Panic, Ref, ToValue, TypeOf, Value, + ValueKind, VmErrorKind, VmResult, +}; use crate::{Any, ContextError, Module}; /// Strings. @@ -27,53 +31,69 @@ use crate::{Any, ContextError, Module}; /// ``` #[rune::module(::std::string)] pub fn module() -> Result { - let mut module = Module::from_meta(self::module_meta)?; - - module.ty::()?; - - module.function_meta(string_from)?; - module - .function_meta(string_from_str)? - .deprecated("Use String::from instead")?; - module.function_meta(string_new)?; - module.function_meta(string_with_capacity)?; - module.function_meta(cmp)?; - module.function_meta(len)?; - module.function_meta(starts_with)?; - module.function_meta(ends_with)?; - module.function_meta(capacity)?; - module.function_meta(clear)?; - module.function_meta(contains)?; - module.function_meta(push)?; - module.function_meta(push_str)?; - module.function_meta(reserve)?; - module.function_meta(reserve_exact)?; - module.function_meta(from_utf8)?; - module.function_meta(as_bytes)?; - module.function_meta(into_bytes)?; - module.function_meta(clone)?; - module.function_meta(shrink_to_fit)?; - module.function_meta(char_at)?; - module.function_meta(split)?; - module.function_meta(split_once)?; - module - .associated_function("split_str", __rune_fn__split)? - .deprecated("Use String::split instead")?; - module.function_meta(trim)?; - module.function_meta(trim_end)?; - module.function_meta(replace)?; - module.function_meta(is_empty)?; - module.function_meta(chars)?; - module.function_meta(get)?; - module.function_meta(parse_int)?; - module.function_meta(parse_char)?; - module.function_meta(to_lowercase)?; - module.function_meta(to_uppercase)?; - - module.function_meta(add)?; - module.function_meta(add_assign)?; - module.function_meta(index_get)?; - Ok(module) + let mut m = Module::from_meta(self::module_meta)?; + + m.ty::()?; + + m.function_meta(string_from)?; + m.function_meta(string_from_str)?; + m.function_meta(string_new)?; + m.function_meta(string_with_capacity)?; + m.function_meta(cmp)?; + m.function_meta(len)?; + m.function_meta(starts_with)?; + m.function_meta(ends_with)?; + m.function_meta(capacity)?; + m.function_meta(clear)?; + m.function_meta(contains)?; + m.function_meta(push)?; + m.function_meta(push_str)?; + m.function_meta(reserve)?; + m.function_meta(reserve_exact)?; + m.function_meta(from_utf8)?; + m.function_meta(as_bytes)?; + m.function_meta(into_bytes)?; + m.function_meta(shrink_to_fit)?; + m.function_meta(char_at)?; + m.function_meta(split)?; + m.function_meta(split_once)?; + m.associated_function("split_str", __rune_fn__split)?; + m.function_meta(trim)?; + m.function_meta(trim_end)?; + m.function_meta(replace)?; + m.function_meta(is_empty)?; + m.function_meta(chars)?; + m.function_meta(get)?; + m.function_meta(parse_int)?; + m.function_meta(parse_char)?; + m.function_meta(to_lowercase)?; + m.function_meta(to_uppercase)?; + + m.function_meta(add)?; + m.function_meta(add_assign)?; + m.function_meta(index_get)?; + + m.function_meta(clone__meta)?; + m.implement_trait::(rune::item!(::std::clone::Clone))?; + + m.ty::()?; + m.function_meta(Chars::next__meta)?; + m.function_meta(Chars::next_back__meta)?; + m.implement_trait::(rune::item!(::std::iter::Iterator))?; + m.implement_trait::(rune::item!(::std::iter::DoubleEndedIterator))?; + + macro_rules! split { + ($ty:ty) => { + m.ty::>()?; + m.function_meta(Split::<$ty>::next__meta)?; + m.implement_trait::>(rune::item!(::std::iter::Iterator))?; + }; + } + + split!(Function); + split!(Box); + split!(char); + Ok(m) } #[derive(Any, Debug, Clone, Copy)] @@ -185,7 +205,7 @@ fn string_from(value: &str) -> VmResult { VmResult::Ok(vm_try!(String::try_from(value))) } -#[rune::function(free, path = String::from_str)] +#[rune::function(free, path = String::from_str, deprecated = "Use String::from instead")] fn string_from_str(value: &str) -> VmResult { VmResult::Ok(vm_try!(String::try_from(value))) } @@ -621,8 +641,7 @@ fn char_at(s: &str, index: usize) -> Option { /// c.push('!'); /// assert_ne!(a, c); /// ``` -#[rune::function(instance)] -#[allow(clippy::ptr_arg)] +#[rune::function(keep, instance, protocol = CLONE)] fn clone(s: &String) -> VmResult { VmResult::Ok(vm_try!(s.try_clone())) } @@ -754,57 +773,20 @@ fn shrink_to_fit(s: &mut String) -> VmResult<()> { /// Use [`split_whitespace`] for this behavior. /// /// [`split_whitespace`]: str::split_whitespace -#[rune::function(instance)] -fn split(this: &str, value: Value) -> VmResult { - const NAME: &str = "std::str::Split"; - - let lines = match *vm_try!(value.borrow_kind_ref()) { +#[rune::function(instance, deprecated = "Use String::split instead")] +fn split(this: Ref, value: Value) -> VmResult { + let split = match *vm_try!(value.borrow_kind_ref()) { ValueKind::String(ref s) => { - let mut out = Vec::new(); - - for value in this.split(s.as_str()) { - let value = vm_try!(String::try_from(value)); - vm_try!(out.try_push(value)); - } - - out + vm_try!(rune::to_value(Split::new( + this, + vm_try!(Box::try_from(s.as_str())) + ))) } - ValueKind::Char(pat) => { - let mut out = Vec::new(); - - for value in this.split(pat) { - let value = vm_try!(String::try_from(value)); - vm_try!(out.try_push(value)); - } - - out + ValueKind::Char(c) => { + vm_try!(rune::to_value(Split::new(this, c))) } ValueKind::Function(ref f) => { - let mut err = None; - - let iter = this.split(|c: char| match f.call::((c,)) { - VmResult::Ok(b) => b, - VmResult::Err(e) => { - if err.is_none() { - err = Some(e); - } - - false - } - }); - - let mut out = Vec::new(); - - for value in iter { - let value = vm_try!(String::try_from(value)); - vm_try!(out.try_push(value)); - } - - if let Some(e) = err.take() { - return VmResult::Err(e); - } - - out + vm_try!(rune::to_value(Split::new(this, vm_try!(f.try_clone())))) } ref actual => { return VmResult::err([ @@ -814,7 +796,7 @@ fn split(this: &str, value: Value) -> VmResult { } }; - VmResult::Ok(Iterator::from_double_ended(NAME, lines.into_iter())) + VmResult::Ok(split) } /// Splits the string on the first occurrence of the specified delimiter and @@ -1016,10 +998,8 @@ fn replace(a: &str, from: &str, to: &str) -> VmResult { /// assert_eq!(None, chars.next()); /// ``` #[rune::function(instance)] -fn chars(s: &str) -> VmResult { - // TODO: perform lazy iteration. - let iter = vm_try!(s.chars().try_collect::>()).into_iter(); - VmResult::Ok(Iterator::from_double_ended("std::str::Chars", iter)) +fn chars(s: Ref) -> Chars { + Chars::new(s) } /// Returns a subslice of `str`. @@ -1285,6 +1265,178 @@ fn to_uppercase(s: &str) -> VmResult { VmResult::Ok(uppercase) } +crate::__internal_impl_any!(::std::string, FromUtf8Error); +crate::__internal_impl_any!(::std::string, Utf8Error); + +#[derive(Any)] +#[rune(item = ::std::string)] +struct Chars { + string: Ref, + start: usize, + end: usize, +} + +impl Chars { + fn new(string: Ref) -> Self { + let end = string.len(); + Self { + string, + start: 0, + end, + } + } + + #[rune::function(keep, protocol = NEXT)] + fn next(&mut self) -> Option { + let string = self.string.get(self.start..self.end)?; + let c = string.chars().next()?; + self.start += c.len_utf8(); + Some(c) + } + + #[rune::function(keep, protocol = NEXT_BACK)] + fn next_back(&mut self) -> Option { + let string = self.string.get(self.start..self.end)?; + let c = string.chars().next_back()?; + self.end -= c.len_utf8(); + Some(c) + } +} + +trait Pattern: 'static + TryClone + Named + FromValue + ToValue + MaybeTypeOf + TypeOf { + fn test(&self, tail: &str) -> VmResult<(bool, usize)>; + + fn is_empty(&self) -> bool; +} + +impl Pattern for Box { + fn test(&self, tail: &str) -> VmResult<(bool, usize)> { + if tail.starts_with(self.as_ref()) { + VmResult::Ok((true, self.len())) + } else { + let Some(c) = tail.chars().next() else { + return VmResult::Ok((false, 0)); + }; + + VmResult::Ok((false, c.len_utf8())) + } + } + + #[inline] + fn is_empty(&self) -> bool { + self.as_ref().is_empty() + } +} + +impl Pattern for char { + fn test(&self, tail: &str) -> VmResult<(bool, usize)> { + let Some(c) = tail.chars().next() else { + return VmResult::Ok((false, 0)); + }; + + VmResult::Ok((c == *self, c.len_utf8())) + } + + #[inline] + fn is_empty(&self) -> bool { + false + } +} + +impl Pattern for Function { + fn test(&self, tail: &str) -> VmResult<(bool, usize)> { + let Some(c) = tail.chars().next() else { + return VmResult::Ok((false, 0)); + }; + + VmResult::Ok((vm_try!(self.call::((c,))), c.len_utf8())) + } + + #[inline] + fn is_empty(&self) -> bool { + false + } +} + +#[derive(Any)] +#[rune(item = ::std::string)] +struct Split +where + T: Pattern, +{ + string: Option>, + pattern: T, + from: usize, + to: usize, +} + +impl Split +where + T: Pattern, +{ + fn new(string: Ref, pattern: T) -> Self { + Self { + string: Some(string), + pattern, + from: 0, + to: 0, + } + } + + #[rune::function(keep, protocol = NEXT)] + fn next(&mut self) -> VmResult> { + let Some(string) = &self.string else { + return VmResult::Ok(None); + }; + + if self.from == string.len() && self.from == self.to { + self.string = None; + let out = vm_try!("".try_to_owned()); + return VmResult::Ok(Some(out)); + } + + while self.to < string.len() { + let Some(tail) = string.get(self.to..) else { + return VmResult::Ok(None); + }; + + let (m, len) = vm_try!(self.pattern.test(tail)); + + if m { + let head = string.get(self.from..self.to).unwrap_or_default(); + let out = vm_try!(head.try_to_owned()); + + if len == 0 { + self.from = self.to; + self.to += tail.chars().next().map_or(0, |c| c.len_utf8()); + } else { + self.to += len; + self.from = self.to; + } + + return VmResult::Ok(Some(out)); + } else { + self.to += len; + } + } + + let tail = string.get(self.from..self.to).unwrap_or_default(); + self.from = self.to; + let out = vm_try!(tail.try_to_owned()); + + if !self.pattern.is_empty() { + self.string = None; + } + + VmResult::Ok(Some(out)) + } + + #[rune::function(keep, protocol = INTO_ITER)] + fn into_iter(self) -> Self { + self + } +} + // Inlined code from core::unicode, since using it directly is marked as using an // unstable library feature mod unicode { @@ -1421,6 +1573,3 @@ mod unicode { } } } - -crate::__internal_impl_any!(::std::string, FromUtf8Error); -crate::__internal_impl_any!(::std::string, Utf8Error); diff --git a/crates/rune/src/modules/test.rs b/crates/rune/src/modules/test.rs index 1de31bc0d..b4258fb75 100644 --- a/crates/rune/src/modules/test.rs +++ b/crates/rune/src/modules/test.rs @@ -12,19 +12,20 @@ use crate::{Any, ContextError, Module, T}; /// Testing and benchmarking. #[rune::module(::std::test)] pub fn module() -> Result { - let mut module = Module::from_meta(self::module_meta)?.with_unique("std::test"); - - module.macro_meta(assert)?; - module.macro_meta(assert_eq)?; - module.macro_meta(assert_ne)?; - - module.ty::()?.docs([ - "A type to perform benchmarks.", - "", - "This is the type of the argument to any function which is annotated with `#[bench]`", - ])?; - module.function_meta(Bencher::iter)?; - Ok(module) + let mut m = Module::from_meta(self::module_meta)?.with_unique("std::test"); + + m.macro_meta(assert)?; + m.macro_meta(assert_eq)?; + m.macro_meta(assert_ne)?; + + m.ty::()?.docs(docstring! { + /// A type to perform benchmarks. + /// + /// This is the type of the argument to any function which is annotated with `#[bench]` + })?; + + m.function_meta(Bencher::iter)?; + Ok(m) } /// A helper type to capture benchmarks. diff --git a/crates/rune/src/modules/tuple.rs b/crates/rune/src/modules/tuple.rs index 71db10870..c7e96105c 100644 --- a/crates/rune/src/modules/tuple.rs +++ b/crates/rune/src/modules/tuple.rs @@ -3,7 +3,8 @@ use core::cmp::Ordering; use crate as rune; -use crate::runtime::{EnvProtocolCaller, Hasher, Iterator, Ref, Tuple, Value, Vec, VmResult}; +use crate::runtime::slice::Iter; +use crate::runtime::{EnvProtocolCaller, Hasher, Ref, Tuple, Value, Vec, VmResult}; use crate::{ContextError, Module}; /// The [`Tuple`] fixed collection. @@ -47,10 +48,19 @@ pub fn module() -> Result { m.function_meta(get)?; m.function_meta(iter)?; m.function_meta(into_iter)?; - m.function_meta(partial_eq)?; - m.function_meta(eq)?; - m.function_meta(partial_cmp)?; - m.function_meta(cmp)?; + + m.function_meta(partial_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + + m.function_meta(eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + + m.function_meta(partial_cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialOrd))?; + + m.function_meta(cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Ord))?; + m.function_meta(hash)?; Ok(m) } @@ -115,8 +125,8 @@ fn get(this: &Tuple, index: Value) -> VmResult> { /// assert_eq!(tuple.iter().collect::(), [1, 2, 3]); /// ``` #[rune::function(instance)] -fn iter(this: Ref) -> Iterator { - Vec::iter_ref(Ref::map(this, |tuple| &**tuple)) +fn iter(this: Ref) -> Iter { + Iter::new(Ref::map(this, |tuple| &**tuple)) } /// Construct an iterator over the tuple. @@ -133,9 +143,9 @@ fn iter(this: Ref) -> Iterator { /// /// assert_eq!(out, [1, 2, 3]); /// ``` -#[rune::function(instance, protocol = INTO_ITER)] -fn into_iter(this: Ref) -> Iterator { - Vec::iter_ref(Ref::map(this, |tuple| &**tuple)) +#[rune::function(instance, instance, protocol = INTO_ITER)] +fn into_iter(this: Ref) -> Iter { + Iter::new(Ref::map(this, |tuple| &**tuple)) } /// Perform a partial equality check with this tuple. @@ -152,7 +162,7 @@ fn into_iter(this: Ref) -> Iterator { /// assert!(tuple == (1..=3)); /// assert!(tuple != (2, 3, 4)); /// ``` -#[rune::function(instance, protocol = PARTIAL_EQ)] +#[rune::function(keep, instance, protocol = PARTIAL_EQ)] fn partial_eq(this: &Tuple, other: Value) -> VmResult { Vec::partial_eq_with(this, other, &mut EnvProtocolCaller) } @@ -169,7 +179,7 @@ fn partial_eq(this: &Tuple, other: Value) -> VmResult { /// assert!(eq(tuple, (1, 2, 3))); /// assert!(!eq(tuple, (2, 3, 4))); /// ``` -#[rune::function(instance, protocol = EQ)] +#[rune::function(keep, instance, protocol = EQ)] fn eq(this: &Tuple, other: &Tuple) -> VmResult { Vec::eq_with(this, other, Value::eq_with, &mut EnvProtocolCaller) } @@ -184,7 +194,7 @@ fn eq(this: &Tuple, other: &Tuple) -> VmResult { /// assert!(tuple > (0, 2, 3)); /// assert!(tuple < (2, 2, 3)); /// ``` -#[rune::function(instance, protocol = PARTIAL_CMP)] +#[rune::function(keep, instance, protocol = PARTIAL_CMP)] fn partial_cmp(this: &Tuple, other: &Tuple) -> VmResult> { Vec::partial_cmp_with(this, other, &mut EnvProtocolCaller) } @@ -202,7 +212,7 @@ fn partial_cmp(this: &Tuple, other: &Tuple) -> VmResult> { /// assert_eq!(cmp(tuple, (0, 2, 3)), Ordering::Greater); /// assert_eq!(cmp(tuple, (2, 2, 3)), Ordering::Less); /// ``` -#[rune::function(instance, protocol = CMP)] +#[rune::function(keep, instance, protocol = CMP)] fn cmp(this: &Tuple, other: &Tuple) -> VmResult { Vec::cmp_with(this, other, &mut EnvProtocolCaller) } diff --git a/crates/rune/src/modules/vec.rs b/crates/rune/src/modules/vec.rs index fc676a733..a007bf58a 100644 --- a/crates/rune/src/modules/vec.rs +++ b/crates/rune/src/modules/vec.rs @@ -4,9 +4,9 @@ use core::cmp::Ordering; use crate as rune; use crate::alloc::prelude::*; +use crate::runtime::slice::Iter; use crate::runtime::{ - EnvProtocolCaller, Formatter, Function, Hasher, Iterator, Ref, TypeOf, Value, Vec, VmErrorKind, - VmResult, + EnvProtocolCaller, Formatter, Function, Hasher, Ref, TypeOf, Value, Vec, VmErrorKind, VmResult, }; use crate::{ContextError, Module}; @@ -45,21 +45,21 @@ use crate::{ContextError, Module}; pub fn module() -> Result { let mut m = Module::from_meta(self::module_meta)?; - m.ty::()?.docs([ - "A dynamic vector.", - "", - "This is the type that is constructed in rune when an array expression such as `[1, 2, 3]` is used.", - "", - "# Comparisons", - "", - "Shorter sequences are considered smaller than longer ones, and vice versa.", - "", - "```rune", - "assert!([1, 2, 3] < [1, 2, 3, 4]);", - "assert!([1, 2, 3] < [1, 2, 4]);", - "assert!([1, 2, 4] > [1, 2, 3]);", - "```", - ])?; + m.ty::()?.docs(docstring! { + /// A dynamic vector. + /// + /// This is the type that is constructed in rune when an array expression such as `[1, 2, 3]` is used. + /// + /// # Comparisons + /// + /// Shorter sequences are considered smaller than longer ones, and vice versa. + /// + /// ```rune + /// assert!([1, 2, 3] < [1, 2, 3, 4]); + /// assert!([1, 2, 3] < [1, 2, 4]); + /// assert!([1, 2, 4] > [1, 2, 3]); + /// ``` + })?; m.function_meta(vec_new)?; m.function_meta(vec_with_capacity)?; @@ -69,22 +69,33 @@ pub fn module() -> Result { m.function_meta(get)?; m.function_meta(clear)?; m.function_meta(extend)?; - m.function_meta(iter)?; + m.function_meta(Vec::rune_iter__meta)?; m.function_meta(pop)?; m.function_meta(push)?; m.function_meta(remove)?; m.function_meta(insert)?; - m.function_meta(clone)?; m.function_meta(sort_by)?; m.function_meta(sort)?; - m.function_meta(into_iter)?; + m.function_meta(into_iter__meta)?; m.function_meta(index_set)?; m.function_meta(index_get)?; m.function_meta(string_debug)?; - m.function_meta(partial_eq)?; - m.function_meta(eq)?; - m.function_meta(partial_cmp)?; - m.function_meta(cmp)?; + + m.function_meta(clone__meta)?; + m.implement_trait::(rune::item!(::std::clone::Clone))?; + + m.function_meta(partial_eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialEq))?; + + m.function_meta(eq__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Eq))?; + + m.function_meta(partial_cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::PartialOrd))?; + + m.function_meta(cmp__meta)?; + m.implement_trait::(rune::item!(::std::cmp::Ord))?; + m.function_meta(hash)?; Ok(m) } @@ -348,22 +359,6 @@ fn extend(this: &mut Vec, value: Value) -> VmResult<()> { this.extend(value) } -/// Iterate over the collection. -/// -/// # Examples -/// -/// ```rune -/// let vec = [1, 2, 3, 4]; -/// let it = vec.iter(); -/// -/// assert_eq!(Some(1), it.next()); -/// assert_eq!(Some(4), it.next_back()); -/// ``` -#[rune::function(instance)] -fn iter(this: Ref) -> Iterator { - Vec::iter_ref(Ref::map(this, |vec| &**vec)) -} - /// Removes the last element from a vector and returns it, or [`None`] if it is /// empty. /// @@ -486,7 +481,7 @@ fn insert(this: &mut Vec, index: usize, value: Value) -> VmResult<()> { /// assert_eq!(a, [1, 2, 3]); /// assert_eq!(b, [1, 2, 3, 4]); /// ``` -#[rune::function(instance)] +#[rune::function(keep, instance, protocol = CLONE)] fn clone(this: &Vec) -> VmResult { VmResult::Ok(vm_try!(this.try_clone())) } @@ -505,9 +500,9 @@ fn clone(this: &Vec) -> VmResult { /// /// assert_eq!(out, [1, 2, 3]); /// ``` -#[rune::function(instance, protocol = INTO_ITER)] -fn into_iter(this: Ref) -> Iterator { - Vec::iter_ref(Ref::map(this, |vec| &**vec)) +#[rune::function(keep, instance, protocol = INTO_ITER)] +fn into_iter(this: Ref) -> Iter { + Vec::rune_iter(this) } /// Returns a reference to an element or subslice depending on the type of @@ -594,7 +589,7 @@ fn string_debug(this: &Vec, f: &mut Formatter) -> VmResult<()> { /// assert!(vec == (1..=3)); /// assert!(vec != [2, 3, 4]); /// ``` -#[rune::function(instance, protocol = PARTIAL_EQ)] +#[rune::function(keep, instance, protocol = PARTIAL_EQ)] fn partial_eq(this: &Vec, other: Value) -> VmResult { Vec::partial_eq_with(this, other, &mut EnvProtocolCaller) } @@ -611,7 +606,7 @@ fn partial_eq(this: &Vec, other: Value) -> VmResult { /// assert!(eq(vec, [1, 2, 3])); /// assert!(!eq(vec, [2, 3, 4])); /// ``` -#[rune::function(instance, protocol = EQ)] +#[rune::function(keep, instance, protocol = EQ)] fn eq(this: &Vec, other: &Vec) -> VmResult { Vec::eq_with(this, other, Value::eq_with, &mut EnvProtocolCaller) } @@ -626,7 +621,7 @@ fn eq(this: &Vec, other: &Vec) -> VmResult { /// assert!(vec > [0, 2, 3]); /// assert!(vec < [2, 2, 3]); /// ``` -#[rune::function(instance, protocol = PARTIAL_CMP)] +#[rune::function(keep, instance, protocol = PARTIAL_CMP)] fn partial_cmp(this: &Vec, other: &Vec) -> VmResult> { Vec::partial_cmp_with(this, other, &mut EnvProtocolCaller) } @@ -644,7 +639,7 @@ fn partial_cmp(this: &Vec, other: &Vec) -> VmResult> { /// assert_eq!(cmp(vec, [0, 2, 3]), Ordering::Greater); /// assert_eq!(cmp(vec, [2, 2, 3]), Ordering::Less); /// ``` -#[rune::function(instance, protocol = CMP)] +#[rune::function(keep, instance, protocol = CMP)] fn cmp(this: &Vec, other: &Vec) -> VmResult { Vec::cmp_with(this, other, &mut EnvProtocolCaller) } diff --git a/crates/rune/src/query/mod.rs b/crates/rune/src/query/mod.rs index e824eb3a8..cf5e57716 100644 --- a/crates/rune/src/query/mod.rs +++ b/crates/rune/src/query/mod.rs @@ -11,7 +11,6 @@ pub(crate) use self::query::{MissingId, Query, QueryInner}; use crate as rune; use crate::alloc::path::PathBuf; use crate::alloc::prelude::*; -use crate::alloc::{self, Box, Vec}; use crate::ast; use crate::ast::{Span, Spanned}; use crate::compile::ir; @@ -199,10 +198,6 @@ impl GenericsParameters { pub(crate) fn is_empty(&self) -> bool { self.parameters.iter().all(|p| p.is_none()) } - - pub(crate) fn as_boxed(&self) -> alloc::Result]>> { - self.parameters.iter().copied().try_collect() - } } impl AsRef for GenericsParameters { diff --git a/crates/rune/src/query/query.rs b/crates/rune/src/query/query.rs index 6e28c2cff..937fba029 100644 --- a/crates/rune/src/query/query.rs +++ b/crates/rune/src/query/query.rs @@ -365,16 +365,12 @@ impl<'a, 'arena> Query<'a, 'arena> { )); }; + let item = self.pool.alloc_item(item)?; + let meta = meta::Meta { context: true, hash: meta.hash, - item_meta: ItemMeta { - id: self.gen.next(), - location: Default::default(), - item: self.pool.alloc_item(item)?, - visibility: Default::default(), - module: Default::default(), - }, + item_meta: self.context_item_meta(item), kind: meta.kind.try_clone()?, source: None, parameters, @@ -408,7 +404,7 @@ impl<'a, 'arena> Query<'a, 'arena> { let kind = if !parameters.parameters.is_empty() { ErrorKind::MissingItemParameters { item: self.pool.item(item).try_to_owned()?, - parameters: parameters.as_boxed()?, + parameters: parameters.parameters, } } else { ErrorKind::MissingItem { @@ -1196,7 +1192,7 @@ impl<'a, 'arena> Query<'a, 'arena> { &mut path, )?; - let Some((item_meta, update)) = update else { + let Some(FoundImportStep { item_meta, import }) = update else { continue; }; @@ -1206,8 +1202,8 @@ impl<'a, 'arena> Query<'a, 'arena> { } path.try_push(ImportStep { - location: update.location, - item: self.pool.item(update.target).try_to_owned()?, + location: import.location, + item: self.pool.item(import.target).try_to_owned()?, })?; if !visited.try_insert(self.pool.alloc_item(&item)?)? { @@ -1220,8 +1216,8 @@ impl<'a, 'arena> Query<'a, 'arena> { )); } - module = update.module; - item = self.pool.item(update.target).join(it)?; + module = import.module; + item = self.pool.item(import.target).join(it)?; any_matched = true; continue 'outer; } @@ -1245,15 +1241,48 @@ impl<'a, 'arena> Query<'a, 'arena> { item: ItemId, used: Used, #[cfg(feature = "emit")] path: &mut Vec, - ) -> compile::Result> { + ) -> compile::Result> { // already resolved query. if let Some(meta) = self.inner.meta.get(&(item, Hash::EMPTY)) { return Ok(match meta.kind { - meta::Kind::Import(import) => Some((meta.item_meta, import)), + meta::Kind::Import(import) => Some(FoundImportStep { + item_meta: meta.item_meta, + import, + }), _ => None, }); } + if let Some(metas) = self.context.lookup_meta(self.pool.item(item)) { + for m in metas { + if let meta::Kind::Alias(alias) = &m.kind { + let target = self.pool.alloc_item(&alias.to)?; + + let import = meta::Import { + location: Default::default(), + target, + module, + }; + + let meta = meta::Meta { + context: true, + hash: self.pool.item_type_hash(item), + item_meta: self.context_item_meta(item), + kind: meta::Kind::Import(import), + source: None, + parameters: Hash::EMPTY, + }; + + let item_meta = self.insert_meta(meta).with_span(span)?; + + return Ok(Some(FoundImportStep { + item_meta: *item_meta, + import, + })); + } + } + } + // resolve query. let Some(entry) = self.remove_indexed(span, item)? else { return Ok(None); @@ -1289,7 +1318,21 @@ impl<'a, 'arena> Query<'a, 'arena> { }; let item_meta = self.insert_meta(meta).with_span(span)?; - Ok(Some((*item_meta, import))) + + Ok(Some(FoundImportStep { + item_meta: *item_meta, + import, + })) + } + + fn context_item_meta(&self, item: ItemId) -> ItemMeta { + ItemMeta { + id: self.gen.next(), + location: Default::default(), + item, + visibility: Default::default(), + module: Default::default(), + } } /// Build a single, indexed entry and return its metadata. @@ -1392,6 +1435,7 @@ impl<'a, 'arena> Query<'a, 'arena> { } _ => None, }, + trait_hash: None, is_test: f.is_test, is_bench: f.is_bench, signature: meta::Signature { @@ -1834,3 +1878,8 @@ impl<'a, 'arena> Query<'a, 'arena> { Some(self.inner.captures.get(&hash)?) } } + +struct FoundImportStep { + item_meta: ItemMeta, + import: meta::Import, +} diff --git a/crates/rune/src/runtime/args.rs b/crates/rune/src/runtime/args.rs index 92cd28f73..3b1e6e4c7 100644 --- a/crates/rune/src/runtime/args.rs +++ b/crates/rune/src/runtime/args.rs @@ -89,6 +89,12 @@ where } } +/// Trait for converting arguments into an array. +pub trait FixedArgs { + /// Encode arguments as array. + fn into_array(self) -> VmResult<[Value; N]>; +} + /// Trait for converting arguments onto the stack. pub trait Args { /// Encode arguments onto a stack. @@ -103,6 +109,18 @@ pub trait Args { macro_rules! impl_into_args { ($count:expr $(, $ty:ident $value:ident $_:expr)*) => { + impl<$($ty,)*> FixedArgs<$count> for ($($ty,)*) + where + $($ty: ToValue,)* + { + #[allow(unused)] + fn into_array(self) -> VmResult<[Value; $count]> { + let ($($value,)*) = self; + $(let $value = vm_try!($value.to_value());)* + VmResult::Ok([$($value),*]) + } + } + impl<$($ty,)*> Args for ($($ty,)*) where $($ty: ToValue,)* diff --git a/crates/rune/src/runtime/function.rs b/crates/rune/src/runtime/function.rs index bdd8af40e..8019f18e4 100644 --- a/crates/rune/src/runtime/function.rs +++ b/crates/rune/src/runtime/function.rs @@ -6,7 +6,7 @@ use ::rust_alloc::sync::Arc; use crate as rune; use crate::alloc::prelude::*; use crate::alloc::{self, Box, Vec}; -use crate::module; +use crate::function; use crate::runtime::vm::Isolated; use crate::runtime::{ Args, Call, ConstValue, FromValue, FunctionHandler, InstAddress, Output, OwnedTuple, Rtti, @@ -110,8 +110,8 @@ impl Function { /// ``` pub fn new(f: F) -> Self where - F: module::Function, - K: module::FunctionKind, + F: function::Function, + K: function::FunctionKind, { Self(FunctionImpl { inner: Inner::FnHandler(FnHandler { @@ -127,8 +127,8 @@ impl Function { #[deprecated = "Use Function::new() instead"] pub fn function(f: F) -> Self where - F: module::Function, - K: module::FunctionKind, + F: function::Function, + K: function::FunctionKind, { Self::new(f) } @@ -137,7 +137,7 @@ impl Function { #[deprecated = "Use Function::new() instead"] pub fn async_function(f: F) -> Self where - F: module::Function, + F: function::Function, { Self::new(f) } diff --git a/crates/rune/src/runtime/generator.rs b/crates/rune/src/runtime/generator.rs index 01e78a86a..81cfaf953 100644 --- a/crates/rune/src/runtime/generator.rs +++ b/crates/rune/src/runtime/generator.rs @@ -1,8 +1,9 @@ use core::fmt; use core::iter; +use crate as rune; use crate::alloc::clone::TryClone; -use crate::runtime::{GeneratorState, Iterator, Value, Vm, VmErrorKind, VmExecution, VmResult}; +use crate::runtime::{GeneratorState, Value, Vm, VmErrorKind, VmExecution, VmResult}; use crate::Any; /// The return value of a function producing a generator. @@ -12,7 +13,7 @@ use crate::Any; /// # Examples /// /// ```rune -/// use std::ops::Generator; +/// use std::ops::generator::Generator; /// /// fn generate() { /// yield 1; @@ -105,8 +106,8 @@ impl Generator<&mut Vm> { impl Generator { /// Convert into iterator - pub fn rune_iter(self) -> Iterator { - Iterator::from("std::ops::generator::Iter", self.into_iter()) + pub fn rune_iter(self) -> Iter { + self.into_iter() } } @@ -120,15 +121,15 @@ impl IntoIterator for Generator { } } +#[derive(Any)] +#[rune(item = ::std::ops::generator)] pub struct Iter { generator: Generator, } -impl iter::Iterator for Iter { - type Item = VmResult; - - #[inline] - fn next(&mut self) -> Option> { +impl Iter { + #[rune::function(instance, keep, protocol = NEXT)] + pub(crate) fn next(&mut self) -> Option> { match self.generator.next() { VmResult::Ok(Some(value)) => Some(VmResult::Ok(value)), VmResult::Ok(None) => None, @@ -137,6 +138,15 @@ impl iter::Iterator for Iter { } } +impl iter::Iterator for Iter { + type Item = VmResult; + + #[inline] + fn next(&mut self) -> Option> { + Iter::next(self) + } +} + impl fmt::Debug for Generator where T: AsRef + AsMut, diff --git a/crates/rune/src/runtime/inst.rs b/crates/rune/src/runtime/inst.rs index 1a4822efd..34a81a405 100644 --- a/crates/rune/src/runtime/inst.rs +++ b/crates/rune/src/runtime/inst.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use crate as rune; use crate::alloc; use crate::alloc::prelude::*; -use crate::runtime::{Call, FormatSpec, Stack, Type, Value, ValueKind, VmError, VmResult}; +use crate::runtime::{Call, FormatSpec, Memory, Type, Value, ValueKind, VmError, VmResult}; use crate::Hash; /// Pre-canned panic reasons. @@ -1250,10 +1250,10 @@ impl Output { /// # Examples /// /// ``` - /// use rune::runtime::{Output, Stack, ToValue, VmResult, InstAddress}; + /// use rune::runtime::{Output, Memory, ToValue, VmResult, InstAddress}; /// use rune::vm_try; /// - /// fn sum(stack: &mut Stack, addr: InstAddress, args: usize, out: Output) -> VmResult<()> { + /// fn sum(stack: &mut dyn Memory, addr: InstAddress, args: usize, out: Output) -> VmResult<()> { /// let mut number = 0; /// /// for value in vm_try!(stack.slice_at(addr, args)) { @@ -1264,13 +1264,13 @@ impl Output { /// VmResult::Ok(()) /// } #[inline] - pub fn store(self, stack: &mut Stack, o: O) -> VmResult<()> + pub fn store(self, stack: &mut dyn Memory, o: O) -> VmResult<()> where O: IntoOutput>>, { - if let Some(index) = self.as_addr() { + if let Some(addr) = self.as_addr() { let value = vm_try!(o.into_output()); - *vm_try!(stack.at_mut(index)) = vm_try!(value.try_into().map_err(Into::into)); + *vm_try!(stack.at_mut(addr)) = vm_try!(value.try_into().map_err(Into::into)); } VmResult::Ok(()) diff --git a/crates/rune/src/runtime/iterator.rs b/crates/rune/src/runtime/iterator.rs index 87482187f..d4d58d77c 100644 --- a/crates/rune/src/runtime/iterator.rs +++ b/crates/rune/src/runtime/iterator.rs @@ -1,1219 +1,40 @@ -use core::cmp; -use core::fmt; -use core::iter; - -use rust_alloc::boxed::Box; - -use crate as rune; use crate::alloc; -use crate::alloc::prelude::*; -use crate::runtime::{FromValue, Function, Panic, ToValue, Value, VmErrorKind, VmResult}; -use crate::Any; - -// Note: A fair amount of code in this module is duplicated from the Rust -// project under the MIT license. -// -// https://github.com/rust-lang/rust -// -// Copyright 2014-2020 The Rust Project Developers - -/// Internal iterator trait used to build useful internal iterator abstractions, -/// like [Fuse]. -trait RuneIterator: fmt::Debug { - /// Test if the iterator is double-ended. - fn is_double_ended(&self) -> bool; - - /// The length of the remaining iterator. - fn size_hint(&self) -> (usize, Option); - - /// Get the next value out of the iterator. - fn next(&mut self) -> VmResult>; +use crate::compile::meta; - /// Get the next back value out of the iterator. - fn next_back(&mut self) -> VmResult>; - - /// Get the length of the iterator if it is an exact length iterator. - #[inline] - fn len(&self) -> VmResult { - let (lower, upper) = self.size_hint(); - - if !matches!(upper, Some(upper) if lower == upper) { - return VmResult::panic(format!("`{:?}` is not an exact-sized iterator", self)); - } - - VmResult::Ok(lower) - } -} - -/// Fuse the iterator if the expression is `None`. -macro_rules! fuse { - ($self:ident . $iter:ident . $($call:tt)+) => { - match $self.$iter { - Some(ref mut iter) => match vm_try!(iter.$($call)+) { - None => { - $self.$iter = None; - None - } - item => item, - }, - None => None, - } - }; -} - -/// Try an iterator method without fusing, -/// like an inline `.as_mut().and_then(...)` -macro_rules! maybe { - ($self:ident . $iter:ident . $($call:tt)+) => { - match $self.$iter { - Some(ref mut iter) => vm_try!(iter.$($call)+), - None => None, - } - }; -} +use super::{FromValue, MaybeTypeOf, Value, VmResult}; /// An owning iterator. -#[derive(Any)] -#[rune(builtin, static_type = ITERATOR)] -#[rune(from_value = Value::into_iterator, from_value_ref = Value::into_iterator_ref, from_value_mut = Value::into_iterator_mut)] +#[derive(Debug)] pub struct Iterator { - iter: IterRepr, + iter: Value, } impl Iterator { - /// Construct a new owning iterator. - /// - /// The name is only intended to identify the iterator in case of errors. - pub fn from(name: &'static str, iter: T) -> Self - where - T: IteratorTrait, - { - Self { - iter: IterRepr::Iterator(Box::new(IteratorObj { name, iter })), - } - } - - /// Construct a new double-ended owning iterator, with a human-readable - /// name. - /// - /// The name is only intended to identify the iterator in case of errors. - pub fn from_double_ended(name: &'static str, iter: T) -> Self - where - T: DoubleEndedIteratorTrait, - { - Self { - iter: IterRepr::DoubleEndedIterator(Box::new(IteratorObj { name, iter })), - } - } - - #[inline] - pub(crate) fn empty() -> Self { - Self { - iter: IterRepr::Empty, - } - } - - #[inline] - pub(crate) fn once(value: Value) -> Self { - Self { - iter: IterRepr::Once(Some(value)), - } + pub(crate) fn new(iter: Value) -> Self { + Self { iter } } #[inline] - pub(crate) fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() + pub(crate) fn size_hint(&self) -> VmResult<(usize, Option)> { + self.iter.protocol_size_hint() } #[inline] pub(crate) fn next(&mut self) -> VmResult> { - self.iter.next() - } - - #[inline] - pub(crate) fn next_back(&mut self) -> VmResult> { - self.iter.next_back() - } - - #[inline] - pub(crate) fn enumerate(self) -> Self { - Self { - iter: IterRepr::Enumerate(Box::new(Enumerate { - iter: self.iter, - count: 0, - })), - } - } - - #[inline] - pub(crate) fn filter(self, filter: Function) -> Self { - Self { - iter: IterRepr::Filter(Box::new(Filter { - iter: self.iter, - filter, - })), - } - } - - #[inline] - pub(crate) fn filter_map(self, filter_map: Function) -> Self { - Self { - iter: IterRepr::FilterMap(Box::new(FilterMap { - iter: self.iter, - filter_map, - })), - } - } - - #[inline] - pub(crate) fn map(self, map: Function) -> Self { - Self { - iter: IterRepr::Map(Box::new(Map { - iter: self.iter, - map, - })), - } - } - - #[inline] - pub(crate) fn flat_map(self, map: Function) -> Self { - Self { - iter: IterRepr::FlatMap(Box::new(FlatMap { - map: Fuse::new(Map { - iter: self.iter, - map, - }), - frontiter: None, - backiter: None, - })), - } - } - - #[inline] - pub(crate) fn find(&mut self, find: Function) -> VmResult> { - while let Some(value) = vm_try!(self.next()) { - if vm_try!(find.call::((value.clone(),))) { - return VmResult::Ok(Some(value)); - } - } - - VmResult::Ok(None) - } - - #[inline] - pub(crate) fn all(&mut self, find: Function) -> VmResult { - while let Some(value) = vm_try!(self.next()) { - let result = vm_try!(find.call::((value.clone(),))); - - if !result { - return VmResult::Ok(false); - } - } - - VmResult::Ok(true) - } - - #[inline] - pub(crate) fn any(&mut self, find: Function) -> VmResult { - while let Some(value) = vm_try!(self.next()) { - if vm_try!(find.call::((value.clone(),))) { - return VmResult::Ok(true); - } - } - - VmResult::Ok(false) - } - - #[inline] - pub(crate) fn chain(self, other: Value) -> VmResult { - let other = vm_try!(other.into_iter()); - - VmResult::Ok(Self { - iter: IterRepr::Chain(Box::new(Chain { - a: Some(self.iter), - b: Some(other.iter), - })), - }) - } - - #[inline] - pub(crate) fn rev(self) -> VmResult { - if !self.iter.is_double_ended() { - return VmResult::panic(format!("`{:?}` is not a double-ended iterator", self)); - } - - VmResult::Ok(Self { - iter: match self.iter { - // NB: reversing a reversed iterator restores the original - // iterator. - IterRepr::Rev(rev) => rev.iter, - iter => IterRepr::Rev(Box::new(Rev { iter })), - }, - }) - } - - #[inline] - pub(crate) fn skip(self, n: usize) -> Self { - Self { - iter: IterRepr::Skip(Box::new(Skip { iter: self.iter, n })), - } - } - - #[inline] - pub(crate) fn take(self, n: usize) -> Self { - Self { - iter: IterRepr::Take(Box::new(Take { iter: self.iter, n })), - } - } - - #[inline] - pub(crate) fn count(&mut self) -> VmResult { - let mut c = 0; - - while vm_try!(self.iter.next()).is_some() { - c += 1; - } - - VmResult::Ok(c) - } - - #[inline] - pub(crate) fn peekable(self) -> Self { - Self { - iter: match self.iter { - IterRepr::Peekable(peekable) => IterRepr::Peekable(peekable), - iter => IterRepr::Peekable(Box::new(Peekable { iter, peeked: None })), - }, - } - } - - #[inline] - pub(crate) fn peek(&mut self) -> VmResult> { - match &mut self.iter { - IterRepr::Peekable(peekable) => peekable.peek(), - _ => VmResult::err(Panic::custom(vm_try!(format_args!( - "`{:?}` is not a peekable iterator", - self.iter - ) - .try_to_string()))), - } - } - - #[inline] - pub(crate) fn collect(mut self) -> VmResult> - where - T: FromValue, - { - let (cap, _) = self.iter.size_hint(); - let mut vec = vm_try!(alloc::Vec::try_with_capacity(cap)); - - while let Some(value) = vm_try!(self.next()) { - vm_try!(vec.try_push(vm_try!(T::from_value(value)))); - } - - VmResult::Ok(vec) - } - - #[inline] - pub(crate) fn fold(mut self, mut accumulator: Value, f: Function) -> VmResult { - while let Some(value) = vm_try!(self.next()) { - accumulator = vm_try!(f.call((accumulator, value.clone()))); - } - - VmResult::Ok(accumulator) - } - - #[inline] - pub(crate) fn reduce(mut self, f: Function) -> VmResult> { - let Some(mut accumulator) = vm_try!(self.next()) else { - return VmResult::Ok(None); - }; - - while let Some(value) = vm_try!(self.next()) { - accumulator = vm_try!(f.call((accumulator, value.clone()))); - } - - VmResult::Ok(Some(accumulator)) - } - - #[inline] - pub(crate) fn product(mut self) -> VmResult - where - T: FromValue + CheckedOps, - { - let mut product = match vm_try!(self.iter.next()) { - Some(init) => vm_try!(T::from_value(init)), - None => T::ONE, - }; - - while let Some(v) = vm_try!(self.iter.next()) { - let v = vm_try!(T::from_value(v)); - - let Some(out) = product.checked_mul(v) else { - return VmResult::err(VmErrorKind::Overflow); - }; - - product = out; - } - - VmResult::Ok(product) - } - - #[inline] - pub(crate) fn sum(mut self) -> VmResult - where - T: FromValue + CheckedOps, - { - let mut sum = match vm_try!(self.iter.next()) { - Some(init) => vm_try!(T::from_value(init)), - None => T::ZERO, - }; - - while let Some(v) = vm_try!(self.next()) { - let v = vm_try!(T::from_value(v)); - - let Some(out) = sum.checked_add(v) else { - return VmResult::err(VmErrorKind::Overflow); - }; - - sum = out; - } - - VmResult::Ok(sum) - } -} - -impl fmt::Debug for Iterator { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.iter, f) - } -} - -/// The inner representation of an [Iterator]. It handles all the necessary -/// dynamic dispatch to support dynamic iterators. -enum IterRepr { - Iterator(Box>), - DoubleEndedIterator(Box>), - Map(Box>), - FlatMap(Box>>), - Filter(Box>), - FilterMap(Box>), - Rev(Box>), - Chain(Box>), - Enumerate(Box>), - Skip(Box>), - Take(Box>), - Peekable(Box>), - Empty, - Once(Option), -} - -impl RuneIterator for IterRepr { - /// Test if this iterator is double-ended. - fn is_double_ended(&self) -> bool { - match self { - Self::Iterator(..) => false, - Self::DoubleEndedIterator(..) => true, - Self::Map(iter) => iter.is_double_ended(), - Self::FlatMap(iter) => iter.is_double_ended(), - Self::Filter(iter) => iter.is_double_ended(), - Self::FilterMap(iter) => iter.is_double_ended(), - Self::Rev(..) => true, - Self::Chain(iter) => iter.is_double_ended(), - Self::Enumerate(iter) => iter.is_double_ended(), - Self::Skip(iter) => iter.is_double_ended(), - Self::Take(iter) => iter.is_double_ended(), - Self::Peekable(iter) => iter.is_double_ended(), - Self::Empty => true, - Self::Once(..) => true, - } - } - - /// The length of the remaining iterator. - fn size_hint(&self) -> (usize, Option) { - match self { - Self::Iterator(iter) => iter.iter.size_hint(), - Self::DoubleEndedIterator(iter) => iter.iter.size_hint(), - Self::Map(iter) => iter.size_hint(), - Self::FlatMap(iter) => iter.size_hint(), - Self::Filter(iter) => iter.size_hint(), - Self::FilterMap(iter) => iter.size_hint(), - Self::Rev(iter) => iter.size_hint(), - Self::Chain(iter) => iter.size_hint(), - Self::Enumerate(iter) => iter.size_hint(), - Self::Skip(iter) => iter.size_hint(), - Self::Take(iter) => iter.size_hint(), - Self::Peekable(iter) => iter.size_hint(), - Self::Empty => (0, Some(0)), - Self::Once(..) => (1, Some(1)), - } - } - - fn next(&mut self) -> VmResult> { - match self { - Self::Iterator(iter) => iter.iter.next(), - Self::DoubleEndedIterator(iter) => iter.iter.next(), - Self::Map(iter) => iter.next(), - Self::FlatMap(iter) => iter.next(), - Self::Filter(iter) => iter.next(), - Self::FilterMap(iter) => iter.next(), - Self::Rev(iter) => iter.next(), - Self::Chain(iter) => iter.next(), - Self::Enumerate(iter) => iter.next(), - Self::Skip(iter) => iter.next(), - Self::Take(iter) => iter.next(), - Self::Peekable(iter) => iter.next(), - Self::Empty => VmResult::Ok(None), - Self::Once(v) => VmResult::Ok(v.take()), - } - } - - fn next_back(&mut self) -> VmResult> { - match self { - Self::Iterator(iter) => { - let message = vm_try!(format_args!( - "`{}` is not a double-ended iterator", - iter.name - ) - .try_to_string()); - VmResult::err(Panic::custom(message)) - } - Self::DoubleEndedIterator(iter) => iter.iter.next_back(), - Self::Map(iter) => iter.next_back(), - Self::FlatMap(iter) => iter.next_back(), - Self::Filter(iter) => iter.next_back(), - Self::FilterMap(iter) => iter.next_back(), - Self::Rev(iter) => iter.next_back(), - Self::Chain(iter) => iter.next_back(), - Self::Enumerate(iter) => iter.next_back(), - Self::Skip(iter) => iter.next_back(), - Self::Take(iter) => iter.next_back(), - Self::Peekable(iter) => iter.next_back(), - Self::Empty => VmResult::Ok(None), - Self::Once(v) => VmResult::Ok(v.take()), - } - } -} - -impl fmt::Debug for IterRepr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Iterator(iter) => write!(f, "{}", iter.name), - Self::DoubleEndedIterator(iter) => write!(f, "{}", iter.name), - Self::Map(iter) => write!(f, "{:?}", iter), - Self::FlatMap(iter) => write!(f, "{:?}", iter), - Self::Filter(iter) => write!(f, "{:?}", iter), - Self::FilterMap(iter) => write!(f, "{:?}", iter), - Self::Rev(iter) => write!(f, "{:?}", iter), - Self::Chain(iter) => write!(f, "{:?}", iter), - Self::Enumerate(iter) => write!(f, "{:?}", iter), - Self::Skip(iter) => write!(f, "{:?}", iter), - Self::Take(iter) => write!(f, "{:?}", iter), - Self::Peekable(iter) => write!(f, "{:?}", iter), - Self::Empty => write!(f, "std::iter::Empty"), - Self::Once(..) => write!(f, "std::iter::Once"), - } - } -} - -#[derive(Debug)] -struct Map { - iter: I, - map: Function, -} - -impl RuneIterator for Map -where - I: RuneIterator, -{ - fn is_double_ended(&self) -> bool { - self.iter.is_double_ended() - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - fn next(&mut self) -> VmResult> { - if let Some(value) = vm_try!(self.iter.next()) { - let out = vm_try!(self.map.call::((value,))); - return VmResult::Ok(Some(out)); - } - - VmResult::Ok(None) - } - - fn next_back(&mut self) -> VmResult> { - if let Some(value) = vm_try!(self.iter.next_back()) { - let out = vm_try!(self.map.call::((value,))); - return VmResult::Ok(Some(out)); - } - - VmResult::Ok(None) - } -} - -#[derive(Debug)] -struct FlatMap { - map: Fuse, - frontiter: Option, - backiter: Option, -} - -impl RuneIterator for FlatMap -where - I: RuneIterator, -{ - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (flo, fhi) = self - .frontiter - .as_ref() - .map_or((0, Some(0)), IterRepr::size_hint); - - let (blo, bhi) = self - .backiter - .as_ref() - .map_or((0, Some(0)), IterRepr::size_hint); - - let lo = flo.saturating_add(blo); - - match (self.map.size_hint(), fhi, bhi) { - ((0, Some(0)), Some(a), Some(b)) => (lo, a.checked_add(b)), - _ => (lo, None), - } - } - - fn is_double_ended(&self) -> bool { - if !self.map.is_double_ended() { - return false; - } - - if !matches!(&self.frontiter, Some(iter) if !iter.is_double_ended()) { - return false; - } - - if !matches!(&self.backiter, Some(iter) if !iter.is_double_ended()) { - return false; - } - - true - } - - fn next(&mut self) -> VmResult> { - loop { - if let Some(iter) = &mut self.frontiter { - match vm_try!(iter.next()) { - None => self.frontiter = None, - item @ Some(_) => return VmResult::Ok(item), - } - } - - match vm_try!(self.map.next()) { - None => { - return VmResult::Ok(match &mut self.backiter { - Some(backiter) => vm_try!(backiter.next()), - None => None, - }) - } - Some(value) => { - let iterator = vm_try!(value.into_iter()); - self.frontiter = Some(iterator.iter) - } - } - } - } - - fn next_back(&mut self) -> VmResult> { - loop { - if let Some(ref mut iter) = self.backiter { - match vm_try!(iter.next_back()) { - None => self.backiter = None, - item @ Some(_) => return VmResult::Ok(item), - } - } - - match vm_try!(self.map.next_back()) { - None => { - return VmResult::Ok(match &mut self.frontiter { - Some(frontiter) => vm_try!(frontiter.next_back()), - None => None, - }) - } - Some(value) => { - let iterator = vm_try!(value.into_iter()); - self.backiter = Some(iterator.iter); - } - } - } - } -} - -#[derive(Debug)] -struct Filter { - iter: I, - filter: Function, -} - -impl RuneIterator for Filter -where - I: RuneIterator, -{ - fn is_double_ended(&self) -> bool { - self.iter.is_double_ended() - } - - fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.iter.size_hint(); - (0, upper) - } - - fn next(&mut self) -> VmResult> { - while let Some(value) = vm_try!(self.iter.next()) { - if vm_try!(self.filter.call::((value.clone(),))) { - return VmResult::Ok(Some(value)); - } - } - - VmResult::Ok(None) - } - - fn next_back(&mut self) -> VmResult> { - while let Some(value) = vm_try!(self.iter.next_back()) { - if vm_try!(self.filter.call::((value.clone(),))) { - return VmResult::Ok(Some(value)); - } - } - - VmResult::Ok(None) - } -} - -#[derive(Debug)] -struct FilterMap { - iter: I, - filter_map: Function, -} - -impl RuneIterator for FilterMap -where - I: RuneIterator, -{ - fn is_double_ended(&self) -> bool { - self.iter.is_double_ended() + self.iter.protocol_next() } - - fn size_hint(&self) -> (usize, Option) { - let (_, upper) = self.iter.size_hint(); - (0, upper) - } - - fn next(&mut self) -> VmResult> { - while let Some(value) = vm_try!(self.iter.next()) { - if let Some(mapped) = vm_try!(self.filter_map.call::>((value.clone(),))) { - return VmResult::Ok(Some(mapped)); - } - } - - VmResult::Ok(None) - } - - fn next_back(&mut self) -> VmResult> { - while let Some(value) = vm_try!(self.iter.next_back()) { - if let Some(mapped) = vm_try!(self.filter_map.call::>((value.clone(),))) { - return VmResult::Ok(Some(mapped)); - } - } - - VmResult::Ok(None) - } -} - -/// The trait for interacting with an iterator. -/// -/// This has a blanket implementation, and is primarily used to restrict the -/// arguments that can be used in [Iterator::from]. -pub trait IteratorTrait: 'static { - /// Size hint of the iterator. - fn size_hint(&self) -> (usize, Option); - - /// Get the next value out of the iterator. - fn next(&mut self) -> VmResult>; -} - -impl IteratorTrait for T -where - T: 'static, - T: iter::Iterator, - T::Item: ToValue, -{ - #[inline] - fn size_hint(&self) -> (usize, Option) { - iter::Iterator::size_hint(self) - } - - fn next(&mut self) -> VmResult> { - let value = match iter::Iterator::next(self) { - Some(value) => value, - None => return VmResult::Ok(None), - }; - - VmResult::Ok(Some(vm_try!(value.to_value()))) - } -} - -/// The trait for interacting with an iterator. -/// -/// This has a blanket implementation, and is primarily used to restrict the -/// arguments that can be used in [Iterator::from_double_ended]. -pub trait DoubleEndedIteratorTrait: IteratorTrait { - /// Get the next back value out of the iterator. - fn next_back(&mut self) -> VmResult>; -} - -impl DoubleEndedIteratorTrait for T -where - T: 'static, - T: iter::DoubleEndedIterator, - T::Item: ToValue, -{ - fn next_back(&mut self) -> VmResult> { - let value = match iter::DoubleEndedIterator::next_back(self) { - Some(value) => value, - None => return VmResult::Ok(None), - }; - - VmResult::Ok(Some(vm_try!(value.to_value()))) - } -} - -struct IteratorObj -where - T: ?Sized, -{ - name: &'static str, - iter: T, -} - -#[derive(Debug)] -struct Chain { - a: Option, - b: Option, } -impl RuneIterator for Chain -where - A: RuneIterator, - B: RuneIterator, -{ - /// Determine if the chain is double ended. - /// - /// It is only double ended if all remaining iterators are double ended. - - fn is_double_ended(&self) -> bool { - !matches!(&self.a, Some(i) if !i.is_double_ended()) - && !matches!(&self.b, Some(i) if !i.is_double_ended()) - } - - fn size_hint(&self) -> (usize, Option) { - match self { - Self { - a: Some(a), - b: Some(b), - } => { - let (a_lower, a_upper) = a.size_hint(); - let (b_lower, b_upper) = b.size_hint(); - - let lower = a_lower.saturating_add(b_lower); - - let upper = match (a_upper, b_upper) { - (Some(x), Some(y)) => x.checked_add(y), - _ => None, - }; - - (lower, upper) - } - Self { - a: Some(a), - b: None, - } => a.size_hint(), - Self { - a: None, - b: Some(b), - } => b.size_hint(), - Self { a: None, b: None } => (0, Some(0)), - } - } - - #[inline] - fn next(&mut self) -> VmResult> { - VmResult::Ok(match fuse!(self.a.next()) { - None => maybe!(self.b.next()), - item => item, - }) - } - +impl FromValue for Iterator { #[inline] - fn next_back(&mut self) -> VmResult> { - VmResult::Ok(match fuse!(self.b.next_back()) { - None => maybe!(self.a.next_back()), - item => item, - }) + fn from_value(value: Value) -> VmResult { + value.into_iter() } } -#[derive(Debug)] -struct Enumerate { - iter: I, - count: usize, -} - -impl RuneIterator for Enumerate -where - I: RuneIterator, -{ - fn is_double_ended(&self) -> bool { - self.iter.is_double_ended() - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - #[inline] - fn next(&mut self) -> VmResult> { - let value = match vm_try!(self.iter.next()) { - Some(value) => value, - None => return VmResult::Ok(None), - }; - - let index = self.count; - self.count = self.count.saturating_add(1); - VmResult::Ok(Some(vm_try!((index, value).to_value()))) - } - - #[inline] - fn next_back(&mut self) -> VmResult> { - let value = match vm_try!(self.iter.next_back()) { - Some(value) => value, - None => return VmResult::Ok(None), - }; - - let len = vm_try!(self.iter.len()); - VmResult::Ok(Some(vm_try!((self.count + len, value).to_value()))) - } -} - -#[derive(Debug)] -#[repr(transparent)] -struct Rev { - iter: I, -} - -impl RuneIterator for Rev -where - I: RuneIterator, -{ - #[inline] - fn is_double_ended(&self) -> bool { - true - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - #[inline] - fn next(&mut self) -> VmResult> { - self.iter.next_back() - } - - #[inline] - fn next_back(&mut self) -> VmResult> { - self.iter.next() - } -} - -#[derive(Debug)] -struct Skip { - iter: I, - n: usize, -} - -impl RuneIterator for Skip -where - I: RuneIterator, -{ - #[inline] - fn is_double_ended(&self) -> bool { - self.iter.is_double_ended() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (lower, upper) = self.iter.size_hint(); - - let lower = lower.saturating_sub(self.n); - let upper = upper.map(|x| x.saturating_sub(self.n)); - - (lower, upper) - } - - #[inline] - fn next(&mut self) -> VmResult> { - if self.n > 0 { - let old_n = self.n; - self.n = 0; - - for _ in 0..old_n { - match vm_try!(self.iter.next()) { - Some(..) => (), - None => return VmResult::Ok(None), - } - } - } - - self.iter.next() - } - - #[inline] - fn next_back(&mut self) -> VmResult> { - VmResult::Ok(if vm_try!(self.len()) > 0 { - vm_try!(self.iter.next_back()) - } else { - None - }) - } -} - -#[derive(Debug)] -struct Take { - iter: I, - n: usize, -} - -impl RuneIterator for Take -where - I: RuneIterator, -{ - #[inline] - fn is_double_ended(&self) -> bool { - self.iter.is_double_ended() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - if self.n == 0 { - return (0, Some(0)); - } - - let (lower, upper) = self.iter.size_hint(); - - let lower = cmp::min(lower, self.n); - - let upper = match upper { - Some(x) if x < self.n => Some(x), - _ => Some(self.n), - }; - - (lower, upper) - } - - #[inline] - fn next(&mut self) -> VmResult> { - if self.n == 0 { - return VmResult::Ok(None); - } - - self.n -= 1; - self.iter.next() - } - - #[inline] - fn next_back(&mut self) -> VmResult> { - if self.n == 0 { - return VmResult::Ok(None); - } - - self.n -= 1; - self.iter.next_back() - } -} - -#[derive(Debug)] -struct Peekable { - iter: I, - peeked: Option>, -} - -impl Peekable -where - I: RuneIterator, -{ - #[inline] - fn peek(&mut self) -> VmResult> { - if let Some(value) = &self.peeked { - return VmResult::Ok(value.clone()); - } - - let value = vm_try!(self.iter.next()); - self.peeked = Some(value.clone()); - VmResult::Ok(value) - } -} - -impl Peekable -where - I: RuneIterator, -{ - #[inline] - fn is_double_ended(&self) -> bool { - self.iter.is_double_ended() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let peek_len = match self.peeked { - Some(None) => return (0, Some(0)), - Some(Some(_)) => 1, - None => 0, - }; - let (lo, hi) = self.iter.size_hint(); - let lo = lo.saturating_add(peek_len); - let hi = match hi { - Some(x) => x.checked_add(peek_len), - None => None, - }; - (lo, hi) - } - - #[inline] - fn next(&mut self) -> VmResult> { - match self.peeked.take() { - Some(v) => VmResult::Ok(v), - None => self.iter.next(), - } - } - - #[inline] - fn next_back(&mut self) -> VmResult> { - match self.peeked.as_mut() { - Some(v @ Some(_)) => VmResult::Ok(vm_try!(self.iter.next_back()).or_else(|| v.take())), - Some(None) => VmResult::Ok(None), - None => self.iter.next_back(), - } - } -} - -#[derive(Debug)] -struct Fuse { - iter: Option, -} - -impl Fuse { - fn new(iter: I) -> Self { - Self { iter: Some(iter) } - } -} - -impl RuneIterator for Fuse -where - I: RuneIterator, -{ - #[inline] - fn is_double_ended(&self) -> bool { - match &self.iter { - Some(iter) => iter.is_double_ended(), - // NB: trivially double-ended since it produces no values. - None => true, - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - match &self.iter { - Some(iter) => iter.size_hint(), - None => (0, Some(0)), - } - } - - #[inline] - fn next(&mut self) -> VmResult> { - if let Some(iter) = &mut self.iter { - if let Some(value) = vm_try!(iter.next()) { - return VmResult::Ok(Some(value)); - } - - self.iter = None; - } - - VmResult::Ok(None) - } - - #[inline] - fn next_back(&mut self) -> VmResult> { - if let Some(iter) = &mut self.iter { - if let Some(value) = vm_try!(iter.next_back()) { - return VmResult::Ok(Some(value)); - } - - self.iter = None; - } - - VmResult::Ok(None) - } -} - -pub(crate) trait CheckedOps: Sized { - const ONE: Self; - const ZERO: Self; - - fn checked_add(self, value: Self) -> Option; - fn checked_mul(self, value: Self) -> Option; -} - -impl CheckedOps for u8 { - const ONE: Self = 1; - const ZERO: Self = 0; - - #[inline] - fn checked_add(self, value: Self) -> Option { - u8::checked_add(self, value) - } - - #[inline] - fn checked_mul(self, value: Self) -> Option { - u8::checked_mul(self, value) - } -} - -impl CheckedOps for i64 { - const ONE: Self = 1; - const ZERO: Self = 0; - - #[inline] - fn checked_add(self, value: Self) -> Option { - i64::checked_add(self, value) - } - - #[inline] - fn checked_mul(self, value: Self) -> Option { - i64::checked_mul(self, value) - } -} - -impl CheckedOps for f64 { - const ONE: Self = 1.0; - const ZERO: Self = 0.0; - - #[inline] - fn checked_add(self, value: Self) -> Option { - Some(self + value) - } - +impl MaybeTypeOf for Iterator { #[inline] - fn checked_mul(self, value: Self) -> Option { - Some(self * value) + fn maybe_type_of() -> alloc::Result { + Ok(meta::DocType::empty()) } } diff --git a/crates/rune/src/runtime/macros.rs b/crates/rune/src/runtime/macros.rs new file mode 100644 index 000000000..7f9a28fca --- /dev/null +++ b/crates/rune/src/runtime/macros.rs @@ -0,0 +1,88 @@ +macro_rules! range_iter { + ($range:ident, $name:ident<$ty:ident> $(, { $($item:tt)* })?) => { + #[derive(Any)] + #[rune(item = ::std::ops)] + pub(crate) struct $name<$ty> + where + $ty: 'static + $crate::alloc::clone::TryClone, + $ty: $crate::compile::Named, + $ty: $crate::runtime::FromValue + $crate::runtime::ToValue, + $ty: $crate::runtime::MaybeTypeOf + $crate::runtime::TypeOf, + { + iter: ::core::ops::$range<$ty>, + } + + impl<$ty> $name<$ty> + where + $ty: 'static + $crate::alloc::clone::TryClone, + $ty: $crate::compile::Named, + $ty: $crate::runtime::FromValue + $crate::runtime::ToValue, + $ty: $crate::runtime::MaybeTypeOf + $crate::runtime::TypeOf, + ::core::ops::$range<$ty>: Iterator, + { + #[inline] + pub(crate) fn new(iter: ::core::ops::$range<$ty>) -> Self { + Self { iter } + } + + #[rune::function(instance, keep, protocol = NEXT)] + #[inline] + pub(crate) fn next(&mut self) -> Option<$ty> { + self.iter.next() + } + + $($($item)*)* + } + + impl<$ty> Iterator for $name<$ty> + where + $ty: 'static + $crate::alloc::clone::TryClone, + $ty: $crate::compile::Named, + $ty: $crate::runtime::FromValue + $crate::runtime::ToValue, + $ty: $crate::runtime::MaybeTypeOf + $crate::runtime::TypeOf, + ::core::ops::$range<$ty>: Iterator, + { + type Item = $ty; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next() + } + } + }; +} + +macro_rules! double_ended_range_iter { + ($range:ident, $name:ident<$ty:ident> $(, { $($item:tt)* })?) => { + range_iter!($range, $name<$ty> $(, { $($item)* })*); + + impl $name + where + T: 'static + $crate::alloc::clone::TryClone, + T: $crate::compile::Named, + T: $crate::runtime::FromValue + $crate::runtime::ToValue, + T: $crate::runtime::MaybeTypeOf + $crate::runtime::TypeOf, + ::core::ops::$range: DoubleEndedIterator, + { + #[rune::function(instance, keep, protocol = NEXT_BACK)] + #[inline] + pub(crate) fn next_back(&mut self) -> Option { + self.iter.next_back() + } + } + + impl DoubleEndedIterator for $name + where + T: 'static + $crate::alloc::clone::TryClone, + T: $crate::compile::Named, + T: $crate::runtime::FromValue + $crate::runtime::ToValue, + T: $crate::runtime::MaybeTypeOf + $crate::runtime::TypeOf, + ::core::ops::$range: DoubleEndedIterator, + { + #[inline] + fn next_back(&mut self) -> Option { + self.iter.next_back() + } + } + }; +} diff --git a/crates/rune/src/runtime/mod.rs b/crates/rune/src/runtime/mod.rs index fecc1b749..12d29f7a4 100644 --- a/crates/rune/src/runtime/mod.rs +++ b/crates/rune/src/runtime/mod.rs @@ -3,6 +3,12 @@ #[cfg(test)] mod tests; +#[macro_use] +mod macros; + +mod steps_between; +use self::steps_between::StepsBetween; + mod access; pub(crate) use self::access::{Access, AccessErrorKind, RawAccessGuard}; pub use self::access::{AccessError, BorrowMut, BorrowRef, Snapshot}; @@ -11,7 +17,7 @@ mod any_obj; pub use self::any_obj::{AnyObj, AnyObjError, AnyObjVtable}; mod args; -pub use self::args::Args; +pub use self::args::{Args, FixedArgs}; pub(crate) use self::args::{DynArgs, DynArgsUsed, DynGuardedArgs}; mod awaited; @@ -48,7 +54,7 @@ mod future; pub use self::future::Future; pub(crate) use self::future::SelectFuture; -mod generator; +pub(crate) mod generator; pub use self::generator::Generator; mod generator_state; @@ -64,7 +70,7 @@ pub use self::inst::{ }; mod iterator; -pub use self::iterator::{Iterator, IteratorTrait}; +pub use self::iterator::Iterator; mod type_; pub use self::type_::Type; @@ -73,7 +79,7 @@ mod label; pub use self::label::DebugLabel; pub(crate) use self::label::Label; -mod object; +pub(crate) mod object; pub use self::object::Object; mod panic; @@ -85,7 +91,7 @@ pub use self::protocol::Protocol; mod protocol_caller; pub(crate) use self::protocol_caller::{EnvProtocolCaller, ProtocolCaller}; -mod range_from; +pub(crate) mod range_from; pub use self::range_from::RangeFrom; mod range_full; @@ -97,18 +103,18 @@ pub use self::range_to_inclusive::RangeToInclusive; mod range_to; pub use self::range_to::RangeTo; -mod range_inclusive; +pub(crate) mod range_inclusive; pub use self::range_inclusive::RangeInclusive; -mod range; +pub(crate) mod range; pub use self::range::Range; #[doc(inline)] pub use rune_core::raw_str::RawStr; mod runtime_context; +pub(crate) use self::runtime_context::FunctionHandler; pub use self::runtime_context::RuntimeContext; -pub(crate) use self::runtime_context::{AttributeMacroHandler, FunctionHandler, MacroHandler}; mod select; pub(crate) use self::select::Select; @@ -118,7 +124,7 @@ pub(crate) use self::shared::Shared; pub use self::shared::{Mut, RawMut, RawRef, Ref, SharedPointerGuard}; mod stack; -pub use self::stack::{SliceError, Stack, StackError}; +pub use self::stack::{Memory, SliceError, Stack, StackError}; mod static_string; pub use self::static_string::StaticString; @@ -139,7 +145,7 @@ mod type_info; pub use self::type_info::{AnyTypeInfo, TypeInfo}; mod type_of; -pub use self::type_of::{MaybeTypeOf, TypeOf}; +pub use self::type_of::{CoreTypeOf, MaybeTypeOf, TypeOf}; pub mod unit; pub(crate) use self::unit::UnitFn; @@ -152,6 +158,8 @@ pub use self::value::{EmptyStruct, Rtti, Struct, TupleStruct, TypeValue, Value, mod variant; pub use self::variant::{Variant, VariantData}; +pub mod slice; + mod vec; pub use self::vec::Vec; diff --git a/crates/rune/src/runtime/object.rs b/crates/rune/src/runtime/object.rs index b2ee94c50..794ff0d6e 100644 --- a/crates/rune/src/runtime/object.rs +++ b/crates/rune/src/runtime/object.rs @@ -5,13 +5,12 @@ use core::fmt; use core::hash; use core::iter; +use crate as rune; use crate::alloc::hashbrown::raw::RawIter; use crate::alloc::prelude::*; use crate::alloc::{self, String}; use crate::alloc::{hash_map, HashMap}; - -use crate as rune; -use crate::runtime::{FromValue, Iterator, ProtocolCaller, RawRef, Ref, ToValue, Value, VmResult}; +use crate::runtime::{FromValue, ProtocolCaller, RawRef, Ref, ToValue, Value, VmResult}; use crate::{Any, ItemBuf}; /// An owning iterator over the entries of a `Object`. @@ -306,38 +305,60 @@ impl Object { /// assert_eq!(vec, [("a", 1), ("b", 2), ("c", 3)]); /// ``` #[rune::function(keep, path = Self::iter)] - pub fn rune_iter(this: Ref) -> Iterator { - struct Iter { - iter: RawIter<(String, Value)>, - _guard: RawRef, - } - - impl iter::Iterator for Iter { - type Item = VmResult<(String, Value)>; - - fn next(&mut self) -> Option { - unsafe { - let bucket = self.iter.next()?; - let (key, value) = bucket.as_ref(); - - let key = match key.try_clone() { - Ok(key) => key, - Err(err) => return Some(VmResult::err(err)), - }; - - Some(VmResult::Ok((key, value.clone()))) - } - } - } - + pub fn rune_iter(this: Ref) -> RuneIter { // SAFETY: we're holding onto the related reference guard, and making // sure that it's dropped after the iterator. let iter = unsafe { this.inner.raw_table().iter() }; let (_, _guard) = Ref::into_raw(this); + RuneIter { iter, _guard } + } - let iter = Iter { iter, _guard }; + /// An iterator visiting all keys in arbitrary order. + /// + /// # Examples + /// + /// ```rune + /// let object = #{a: 1, b: 2, c: 3}; + /// let vec = []; + /// + /// for key in object.keys() { + /// vec.push(key); + /// } + /// + /// vec.sort_by(|a, b| a.cmp(b)); + /// assert_eq!(vec, ["a", "b", "c"]); + /// ``` + #[rune::function(keep, path = Self::keys)] + pub fn rune_keys(this: Ref) -> RuneIterKeys { + // SAFETY: we're holding onto the related reference guard, and making + // sure that it's dropped after the iterator. + let iter = unsafe { this.inner.raw_table().iter() }; + let (_, _guard) = Ref::into_raw(this); + RuneIterKeys { iter, _guard } + } - Iterator::from("std::object::Iter", iter) + /// An iterator visiting all values in arbitrary order. + /// + /// # Examples + /// + /// ```rune + /// let object = #{a: 1, b: 2, c: 3}; + /// let vec = []; + /// + /// for key in object.values() { + /// vec.push(key); + /// } + /// + /// vec.sort_by(|a, b| a.cmp(b)); + /// assert_eq!(vec, [1, 2, 3]); + /// ``` + #[rune::function(keep, path = Self::values)] + pub fn rune_values(this: Ref) -> RuneValues { + // SAFETY: we're holding onto the related reference guard, and making + // sure that it's dropped after the iterator. + let iter = unsafe { this.inner.raw_table().iter() }; + let (_, _guard) = Ref::into_raw(this); + RuneValues { iter, _guard } } pub(crate) fn partial_eq_with( @@ -524,3 +545,126 @@ impl fmt::Display for DebugStruct<'_> { d.finish() } } + +#[derive(Any)] +#[rune(item = ::std::object, name = Iter)] +pub struct RuneIter { + iter: RawIter<(String, Value)>, + _guard: RawRef, +} + +impl RuneIter { + #[rune::function(instance, keep, protocol = NEXT)] + pub fn next(&mut self) -> Option> { + unsafe { + let bucket = self.iter.next()?; + let (key, value) = bucket.as_ref(); + + let key = match key.try_clone() { + Ok(key) => key, + Err(err) => return Some(VmResult::err(err)), + }; + + Some(VmResult::Ok((key, value.clone()))) + } + } + + #[rune::function(instance, keep, protocol = SIZE_HINT)] + pub fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[rune::function(instance, keep, protocol = LEN)] + pub fn len(&self) -> usize { + self.iter.len() + } +} + +impl iter::Iterator for RuneIter { + type Item = VmResult<(String, Value)>; + + #[inline] + fn next(&mut self) -> Option { + RuneIter::next(self) + } +} + +#[derive(Any)] +#[rune(item = ::std::object, name = Keys)] +pub struct RuneIterKeys { + iter: RawIter<(String, Value)>, + _guard: RawRef, +} + +impl RuneIterKeys { + #[rune::function(instance, keep, protocol = NEXT)] + pub fn next(&mut self) -> Option> { + unsafe { + let bucket = self.iter.next()?; + let (key, _) = bucket.as_ref(); + + let key = match key.try_clone() { + Ok(key) => key, + Err(err) => return Some(VmResult::err(err)), + }; + + Some(VmResult::Ok(key)) + } + } + + #[rune::function(instance, keep, protocol = SIZE_HINT)] + pub fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[rune::function(instance, keep, protocol = LEN)] + pub fn len(&self) -> usize { + self.iter.len() + } +} + +impl iter::Iterator for RuneIterKeys { + type Item = VmResult; + + #[inline] + fn next(&mut self) -> Option { + RuneIterKeys::next(self) + } +} + +#[derive(Any)] +#[rune(item = ::std::object, name = Values)] +pub struct RuneValues { + iter: RawIter<(String, Value)>, + _guard: RawRef, +} + +impl RuneValues { + #[rune::function(instance, keep, protocol = NEXT)] + pub fn next(&mut self) -> Option> { + unsafe { + let bucket = self.iter.next()?; + let (_, value) = bucket.as_ref(); + Some(VmResult::Ok(value.clone())) + } + } + + #[rune::function(instance, keep, protocol = SIZE_HINT)] + pub fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[rune::function(instance, keep, protocol = LEN)] + pub fn len(&self) -> usize { + self.iter.len() + } +} + +impl iter::Iterator for RuneValues { + type Item = VmResult; + + #[inline] + fn next(&mut self) -> Option { + RuneValues::next(self) + } +} diff --git a/crates/rune/src/runtime/range.rs b/crates/rune/src/runtime/range.rs index 5bd8127fb..c11a7883d 100644 --- a/crates/rune/src/runtime/range.rs +++ b/crates/rune/src/runtime/range.rs @@ -5,11 +5,12 @@ use core::ops; use crate as rune; use crate::alloc::clone::TryClone; use crate::runtime::{ - EnvProtocolCaller, FromValue, Iterator, ProtocolCaller, ToValue, Value, ValueKind, VmErrorKind, - VmResult, + EnvProtocolCaller, FromValue, ProtocolCaller, ToValue, Value, ValueKind, VmErrorKind, VmResult, }; use crate::Any; +use super::StepsBetween; + /// Type for a range expression `start..end`. /// /// # Examples @@ -92,27 +93,29 @@ impl Range { /// range.iter() /// ``` #[rune::function(keep)] - pub fn iter(&self) -> VmResult { - const NAME: &str = "std::ops::Range"; - - match ( + pub fn iter(&self) -> VmResult { + let value = match ( &*vm_try!(self.start.borrow_kind_ref()), &*vm_try!(self.end.borrow_kind_ref()), ) { (ValueKind::Byte(start), ValueKind::Byte(end)) => { - VmResult::Ok(Iterator::from_double_ended(NAME, *start..*end)) + vm_try!(rune::to_value(RangeIter::new(*start..*end))) } (ValueKind::Char(start), ValueKind::Char(end)) => { - VmResult::Ok(Iterator::from_double_ended(NAME, *start..*end)) + vm_try!(rune::to_value(RangeIter::new(*start..*end))) } (ValueKind::Integer(start), ValueKind::Integer(end)) => { - VmResult::Ok(Iterator::from_double_ended(NAME, *start..*end)) + vm_try!(rune::to_value(RangeIter::new(*start..*end))) } - (start, end) => VmResult::err(VmErrorKind::UnsupportedIterRange { - start: start.type_info(), - end: end.type_info(), - }), - } + (start, end) => { + return VmResult::err(VmErrorKind::UnsupportedIterRange { + start: start.type_info(), + end: end.type_info(), + }) + } + }; + + VmResult::Ok(value) } /// Iterate over the range. @@ -140,7 +143,7 @@ impl Range { /// } /// ``` #[rune::function(keep, protocol = INTO_ITER)] - pub fn into_iter(&self) -> VmResult { + pub fn into_iter(&self) -> VmResult { self.iter() } @@ -320,3 +323,27 @@ where VmResult::Ok(ops::Range { start, end }) } } + +double_ended_range_iter!(Range, RangeIter, { + #[rune::function(instance, keep, protocol = SIZE_HINT)] + #[inline] + pub(crate) fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[rune::function(instance, keep, protocol = LEN)] + #[inline] + pub(crate) fn len(&self) -> VmResult + where + T: Copy + StepsBetween + fmt::Debug, + { + let Some(result) = T::steps_between(self.iter.start, self.iter.end) else { + return VmResult::panic(format!( + "could not calculate length of range {:?}..={:?}", + self.iter.start, self.iter.end + )); + }; + + VmResult::Ok(result) + } +}); diff --git a/crates/rune/src/runtime/range_from.rs b/crates/rune/src/runtime/range_from.rs index 23fa0b8f8..cceccaabb 100644 --- a/crates/rune/src/runtime/range_from.rs +++ b/crates/rune/src/runtime/range_from.rs @@ -5,8 +5,7 @@ use core::ops; use crate as rune; use crate::alloc::clone::TryClone; use crate::runtime::{ - EnvProtocolCaller, FromValue, Iterator, ProtocolCaller, ToValue, Value, ValueKind, VmErrorKind, - VmResult, + EnvProtocolCaller, FromValue, ProtocolCaller, ToValue, Value, ValueKind, VmErrorKind, VmResult, }; use crate::Any; @@ -86,17 +85,19 @@ impl RangeFrom { /// range.iter() /// ``` #[rune::function(keep)] - pub fn iter(&self) -> VmResult { - const NAME: &str = "std::ops::RangeFrom"; + pub fn iter(&self) -> VmResult { + let value = match *vm_try!(self.start.borrow_kind_ref()) { + ValueKind::Byte(start) => vm_try!(crate::to_value(RangeFromIter::new(start..))), + ValueKind::Char(start) => vm_try!(crate::to_value(RangeFromIter::new(start..))), + ValueKind::Integer(start) => vm_try!(crate::to_value(RangeFromIter::new(start..))), + ref start => { + return VmResult::err(VmErrorKind::UnsupportedIterRangeFrom { + start: start.type_info(), + }) + } + }; - match *vm_try!(self.start.borrow_kind_ref()) { - ValueKind::Byte(start) => VmResult::Ok(Iterator::from(NAME, start..)), - ValueKind::Char(start) => VmResult::Ok(Iterator::from(NAME, start..)), - ValueKind::Integer(start) => VmResult::Ok(Iterator::from(NAME, start..)), - ref start => VmResult::err(VmErrorKind::UnsupportedIterRangeFrom { - start: start.type_info(), - }), - } + VmResult::Ok(value) } /// Build an iterator over the range. @@ -130,7 +131,7 @@ impl RangeFrom { /// } /// ``` #[rune::function(keep, protocol = INTO_ITER)] - pub fn into_iter(&self) -> VmResult { + pub fn into_iter(&self) -> VmResult { self.iter() } @@ -285,3 +286,11 @@ where VmResult::Ok(ops::RangeFrom { start }) } } + +range_iter!(RangeFrom, RangeFromIter, { + #[rune::function(instance, keep, protocol = SIZE_HINT)] + #[inline] + pub(crate) fn size_hint(&self) -> (i64, Option) { + (i64::MAX, None) + } +}); diff --git a/crates/rune/src/runtime/range_inclusive.rs b/crates/rune/src/runtime/range_inclusive.rs index 68fea416b..fa4f5e8a6 100644 --- a/crates/rune/src/runtime/range_inclusive.rs +++ b/crates/rune/src/runtime/range_inclusive.rs @@ -5,11 +5,12 @@ use core::ops; use crate as rune; use crate::alloc::clone::TryClone; use crate::runtime::{ - EnvProtocolCaller, FromValue, Iterator, ProtocolCaller, ToValue, Value, ValueKind, VmErrorKind, - VmResult, + EnvProtocolCaller, FromValue, ProtocolCaller, ToValue, Value, ValueKind, VmErrorKind, VmResult, }; use crate::Any; +use super::StepsBetween; + /// Type for an inclusive range expression `start..=end`. /// /// # Examples @@ -93,27 +94,29 @@ impl RangeInclusive { /// range.iter() /// ``` #[rune::function(keep)] - pub fn iter(&self) -> VmResult { - const NAME: &str = "std::ops::RangeInclusive"; - - match ( + pub fn iter(&self) -> VmResult { + let value = match ( &*vm_try!(self.start.borrow_kind_ref()), &*vm_try!(self.end.borrow_kind_ref()), ) { (ValueKind::Byte(start), ValueKind::Byte(end)) => { - VmResult::Ok(Iterator::from_double_ended(NAME, *start..=*end)) + vm_try!(rune::to_value(RangeInclusiveIter::new(*start..=*end))) } (ValueKind::Char(start), ValueKind::Char(end)) => { - VmResult::Ok(Iterator::from_double_ended(NAME, *start..=*end)) + vm_try!(rune::to_value(RangeInclusiveIter::new(*start..=*end))) } (ValueKind::Integer(start), ValueKind::Integer(end)) => { - VmResult::Ok(Iterator::from_double_ended(NAME, *start..=*end)) + vm_try!(rune::to_value(RangeInclusiveIter::new(*start..=*end))) } - (start, end) => VmResult::err(VmErrorKind::UnsupportedIterRangeInclusive { - start: start.type_info(), - end: end.type_info(), - }), - } + (start, end) => { + return VmResult::err(VmErrorKind::UnsupportedIterRangeInclusive { + start: start.type_info(), + end: end.type_info(), + }) + } + }; + + VmResult::Ok(value) } /// Iterate over the range. @@ -141,7 +144,7 @@ impl RangeInclusive { /// } /// ``` #[rune::function(keep, protocol = INTO_ITER)] - pub fn into_iter(&self) -> VmResult { + pub fn into_iter(&self) -> VmResult { self.iter() } @@ -321,3 +324,28 @@ where VmResult::Ok(start..=end) } } + +double_ended_range_iter!(RangeInclusive, RangeInclusiveIter, { + #[rune::function(instance, keep, protocol = SIZE_HINT)] + #[inline] + pub(crate) fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[rune::function(instance, keep, protocol = LEN)] + #[inline] + pub(crate) fn len(&self) -> VmResult + where + T: Copy + StepsBetween + fmt::Debug, + { + let Some(result) = T::steps_between(*self.iter.start(), *self.iter.end()) else { + return VmResult::panic(format!( + "could not calculate length of range {:?}..={:?}", + self.iter.start(), + self.iter.end() + )); + }; + + VmResult::Ok(result) + } +}); diff --git a/crates/rune/src/runtime/runtime_context.rs b/crates/rune/src/runtime/runtime_context.rs index 749c8cfb9..725e009c4 100644 --- a/crates/rune/src/runtime/runtime_context.rs +++ b/crates/rune/src/runtime/runtime_context.rs @@ -4,24 +4,13 @@ use ::rust_alloc::sync::Arc; use crate as rune; use crate::alloc::prelude::*; -use crate::compile; use crate::hash; -use crate::macros::{MacroContext, TokenStream}; -use crate::runtime::{ConstValue, InstAddress, Output, Stack, VmResult}; +use crate::runtime::{ConstValue, InstAddress, Memory, Output, VmResult}; use crate::Hash; /// A type-reduced function handler. pub(crate) type FunctionHandler = - dyn Fn(&mut Stack, InstAddress, usize, Output) -> VmResult<()> + Send + Sync; - -/// A (type erased) macro handler. -pub(crate) type MacroHandler = - dyn Fn(&mut MacroContext, &TokenStream) -> compile::Result + Send + Sync; - -/// A (type erased) attribute macro handler. -pub(crate) type AttributeMacroHandler = dyn Fn(&mut MacroContext, &TokenStream, &TokenStream) -> compile::Result - + Send - + Sync; + dyn Fn(&mut dyn Memory, InstAddress, usize, Output) -> VmResult<()> + Send + Sync; /// Static run context visible to the virtual machine. /// diff --git a/crates/rune/src/runtime/slice.rs b/crates/rune/src/runtime/slice.rs new file mode 100644 index 000000000..af6413fe9 --- /dev/null +++ b/crates/rune/src/runtime/slice.rs @@ -0,0 +1,4 @@ +//! Types for working with slices. + +mod iter; +pub(crate) use self::iter::Iter; diff --git a/crates/rune/src/runtime/vec/iter.rs b/crates/rune/src/runtime/slice/iter.rs similarity index 62% rename from crates/rune/src/runtime/vec/iter.rs rename to crates/rune/src/runtime/slice/iter.rs index d32fe5d7f..aac73b4d2 100644 --- a/crates/rune/src/runtime/vec/iter.rs +++ b/crates/rune/src/runtime/slice/iter.rs @@ -1,7 +1,11 @@ +use crate as rune; use crate::runtime::{Ref, Value}; +use crate::Any; /// An efficient reference counter iterator over a vector. -pub(crate) struct Iter { +#[derive(Any)] +#[rune(item = ::std::slice)] +pub struct Iter { vec: Ref<[Value]>, front: usize, back: usize, @@ -10,19 +14,17 @@ pub(crate) struct Iter { impl Iter { pub(crate) fn new(vec: Ref<[Value]>) -> Self { let back = vec.len(); + Self { vec, front: 0, back, } } -} - -impl Iterator for Iter { - type Item = Value; + #[rune::function(instance, keep, protocol = NEXT)] #[inline] - fn next(&mut self) -> Option { + fn next(&mut self) -> Option { if self.front == self.back { return None; } @@ -32,8 +34,9 @@ impl Iterator for Iter { Some(value.clone()) } + #[rune::function(instance, keep, protocol = NTH)] #[inline] - fn nth(&mut self, n: usize) -> Option { + fn nth(&mut self, n: usize) -> Option { let n = self.front.wrapping_add(n); if n >= self.back || n < self.front { @@ -45,16 +48,22 @@ impl Iterator for Iter { Some(value.clone()) } + #[rune::function(instance, keep, protocol = SIZE_HINT)] #[inline] fn size_hint(&self) -> (usize, Option) { let len = self.back.wrapping_sub(self.front); (len, Some(len)) } -} -impl DoubleEndedIterator for Iter { + #[rune::function(instance, keep, protocol = LEN)] #[inline] - fn next_back(&mut self) -> Option { + fn len(&self) -> usize { + self.back.wrapping_sub(self.front) + } + + #[rune::function(instance, keep, protocol = NEXT_BACK)] + #[inline] + fn next_back(&mut self) -> Option { if self.front == self.back { return None; } @@ -64,3 +73,29 @@ impl DoubleEndedIterator for Iter { Some(value.clone()) } } + +impl Iterator for Iter { + type Item = Value; + + #[inline] + fn next(&mut self) -> Option { + Iter::next(self) + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + Iter::nth(self, n) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + Iter::size_hint(self) + } +} + +impl DoubleEndedIterator for Iter { + #[inline] + fn next_back(&mut self) -> Option { + Iter::next_back(self) + } +} diff --git a/crates/rune/src/runtime/stack.rs b/crates/rune/src/runtime/stack.rs index a71b91ec3..63c18b6fa 100644 --- a/crates/rune/src/runtime/stack.rs +++ b/crates/rune/src/runtime/stack.rs @@ -49,6 +49,103 @@ cfg_std! { impl std::error::Error for SliceError {} } +/// Memory access. +pub trait Memory { + /// Get the slice at the given address with the given length. + /// + /// # Examples + /// + /// ``` + /// use rune::vm_try; + /// use rune::Module; + /// use rune::runtime::{Output, Memory, ToValue, VmResult, InstAddress}; + /// + /// fn sum(stack: &mut dyn Memory, addr: InstAddress, args: usize, out: Output) -> VmResult<()> { + /// let mut number = 0; + /// + /// for value in vm_try!(stack.slice_at(addr, args)) { + /// number += vm_try!(value.as_integer()); + /// } + /// + /// out.store(stack, number); + /// VmResult::Ok(()) + /// } + /// ``` + fn slice_at(&self, addr: InstAddress, len: usize) -> Result<&[Value], SliceError>; + + /// Get a value mutable at the given index from the stack bottom. + /// + /// # Examples + /// + /// ``` + /// use rune::vm_try; + /// use rune::Module; + /// use rune::runtime::{Output, Memory, VmResult, InstAddress}; + /// + /// fn add_one(stack: &mut dyn Memory, addr: InstAddress, args: usize, out: Output) -> VmResult<()> { + /// let mut value = vm_try!(stack.at_mut(addr)); + /// let number = vm_try!(value.as_integer()); + /// *value = vm_try!(rune::to_value(number + 1)); + /// out.store(stack, ()); + /// VmResult::Ok(()) + /// } + /// ``` + fn at_mut(&mut self, addr: InstAddress) -> Result<&mut Value, StackError>; + + /// Get the slice at the given address with the given static length. + fn array_at(&self, addr: InstAddress) -> Result<[&Value; N], SliceError> + where + Self: Sized, + { + let slice = self.slice_at(addr, N)?; + Ok(array::from_fn(|i| &slice[i])) + } +} + +impl Memory for &mut M +where + M: Memory + ?Sized, +{ + #[inline] + fn slice_at(&self, addr: InstAddress, len: usize) -> Result<&[Value], SliceError> { + (**self).slice_at(addr, len) + } + + #[inline] + fn at_mut(&mut self, addr: InstAddress) -> Result<&mut Value, StackError> { + (**self).at_mut(addr) + } +} + +impl Memory for [Value; N] { + fn slice_at(&self, addr: InstAddress, len: usize) -> Result<&[Value], SliceError> { + if len == 0 { + return Ok(&[]); + } + + let start = addr.offset(); + + let Some(values) = start.checked_add(len).and_then(|end| self.get(start..end)) else { + return Err(SliceError { + addr, + len, + stack: N, + }); + }; + + Ok(values) + } + + #[inline] + fn at_mut(&mut self, addr: InstAddress) -> Result<&mut Value, StackError> { + let Some(value) = self.get_mut(addr.offset()) else { + return Err(StackError { addr }); + }; + + Ok(value) + } +} + /// The stack of the virtual machine, where all values are stored. #[derive(Default, Debug)] pub struct Stack { @@ -351,6 +448,18 @@ impl Stack { } } +impl Memory for Stack { + #[inline] + fn slice_at(&self, addr: InstAddress, len: usize) -> Result<&[Value], SliceError> { + Stack::slice_at(self, addr, len) + } + + #[inline] + fn at_mut(&mut self, addr: InstAddress) -> Result<&mut Value, StackError> { + Stack::at_mut(self, addr) + } +} + #[inline(always)] fn inner_slice_at(values: &[Value], top: usize, addr: InstAddress, len: usize) -> Option<&[Value]> { if len == 0 { diff --git a/crates/rune/src/runtime/static_type.rs b/crates/rune/src/runtime/static_type.rs index b05abeef6..5e94db035 100644 --- a/crates/rune/src/runtime/static_type.rs +++ b/crates/rune/src/runtime/static_type.rs @@ -114,6 +114,7 @@ pub(crate) static STRING: &StaticType = &StaticType { #[cfg(feature = "alloc")] impl_static_type!(::rust_alloc::string::String => STRING); impl_static_type!(alloc::String => STRING); +impl_static_type!(alloc::Box => STRING); impl_static_type!(str => STRING); pub(crate) static BYTES: &StaticType = &StaticType { @@ -199,12 +200,12 @@ pub(crate) static FUTURE: &StaticType = &StaticType { pub(crate) static GENERATOR: &StaticType = &StaticType { name: RawStr::from_str("Generator"), - hash: ::rune_macros::hash!(::std::ops::Generator), + hash: ::rune_macros::hash!(::std::ops::generator::Generator), }; pub(crate) static GENERATOR_STATE: &StaticType = &StaticType { name: RawStr::from_str("GeneratorState"), - hash: ::rune_macros::hash!(::std::ops::GeneratorState), + hash: ::rune_macros::hash!(::std::ops::generator::GeneratorState), }; pub(crate) static STREAM: &StaticType = &StaticType { @@ -236,11 +237,6 @@ pub(crate) static FORMAT: &StaticType = &StaticType { hash: ::rune_macros::hash!(::std::fmt::Format), }; -pub(crate) static ITERATOR: &StaticType = &StaticType { - name: RawStr::from_str("Iterator"), - hash: ::rune_macros::hash!(::std::iter::Iterator), -}; - pub(crate) static ORDERING: &StaticType = &StaticType { name: RawStr::from_str("Ordering"), hash: ::rune_macros::hash!(::std::cmp::Ordering), diff --git a/crates/rune/src/runtime/steps_between.rs b/crates/rune/src/runtime/steps_between.rs new file mode 100644 index 000000000..73fb16497 --- /dev/null +++ b/crates/rune/src/runtime/steps_between.rs @@ -0,0 +1,17 @@ +pub(crate) trait StepsBetween { + fn steps_between(start: Self, end: Self) -> Option; +} + +impl StepsBetween for i64 { + #[inline] + fn steps_between(start: Self, end: Self) -> Option { + usize::try_from(end.checked_sub(start)?).ok() + } +} + +impl StepsBetween for u8 { + #[inline] + fn steps_between(start: Self, end: Self) -> Option { + Some(usize::from(end.checked_sub(start)?)) + } +} diff --git a/crates/rune/src/runtime/type_of.rs b/crates/rune/src/runtime/type_of.rs index 959736971..08d7cbbd9 100644 --- a/crates/rune/src/runtime/type_of.rs +++ b/crates/rune/src/runtime/type_of.rs @@ -3,17 +3,42 @@ use crate::compile::meta; use crate::runtime::{Mut, Ref, Shared, TypeInfo}; use crate::Hash; +/// Core type of trait. +pub trait CoreTypeOf { + /// Get full type hash, including type parameters. + fn type_hash() -> Hash; +} + +/// Blanket implementation for references. +impl CoreTypeOf for &T +where + T: ?Sized + CoreTypeOf, +{ + #[inline] + fn type_hash() -> Hash { + T::type_hash() + } +} + +/// Blanket implementation for mutable references. +impl CoreTypeOf for &mut T +where + T: ?Sized + CoreTypeOf, +{ + #[inline] + fn type_hash() -> Hash { + T::type_hash() + } +} + /// Trait used for Rust types for which we can determine the runtime type of. -pub trait TypeOf { +pub trait TypeOf: CoreTypeOf { /// Hash of type parameters. #[inline] fn type_parameters() -> Hash { Hash::EMPTY } - /// Get full type hash, including type parameters. - fn type_hash() -> Hash; - /// Access diagnostical information on the value type. fn type_info() -> TypeInfo; } @@ -84,11 +109,6 @@ where T::type_parameters() } - #[inline] - fn type_hash() -> Hash { - T::type_hash() - } - #[inline] fn type_info() -> TypeInfo { T::type_info() @@ -106,13 +126,19 @@ where } #[inline] - fn type_hash() -> Hash { - T::type_hash() + fn type_info() -> TypeInfo { + T::type_info() } +} +/// Blanket implementation for owned references. +impl CoreTypeOf for Ref +where + T: ?Sized + CoreTypeOf, +{ #[inline] - fn type_info() -> TypeInfo { - T::type_info() + fn type_hash() -> Hash { + T::type_hash() } } @@ -127,13 +153,19 @@ where } #[inline] - fn type_hash() -> Hash { - T::type_hash() + fn type_info() -> TypeInfo { + T::type_info() } +} +/// Blanket implementation for owned mutable references. +impl CoreTypeOf for Mut +where + T: ?Sized + CoreTypeOf, +{ #[inline] - fn type_info() -> TypeInfo { - T::type_info() + fn type_hash() -> Hash { + T::type_hash() } } @@ -148,13 +180,19 @@ where } #[inline] - fn type_hash() -> Hash { - T::type_hash() + fn type_info() -> TypeInfo { + T::type_info() } +} +/// Blanket implementation for owned shared values. +impl CoreTypeOf for Shared +where + T: ?Sized + CoreTypeOf, +{ #[inline] - fn type_info() -> TypeInfo { - T::type_info() + fn type_hash() -> Hash { + T::type_hash() } } @@ -168,11 +206,6 @@ where T::type_parameters() } - #[inline] - fn type_hash() -> Hash { - T::type_hash() - } - #[inline] fn type_info() -> TypeInfo { T::type_info() diff --git a/crates/rune/src/runtime/value.rs b/crates/rune/src/runtime/value.rs index 27a719481..57965e39f 100644 --- a/crates/rune/src/runtime/value.rs +++ b/crates/rune/src/runtime/value.rs @@ -892,9 +892,6 @@ impl Value { ValueKind::Format(value) => { vm_write!(f, "{:?}", value); } - ValueKind::Iterator(value) => { - vm_write!(f, "{:?}", value); - } _ => { // reborrow f to avoid moving it let mut args = DynGuardedArgs::new((&mut *f,)); @@ -933,7 +930,7 @@ impl Value { pub(crate) fn into_iter_with(self, caller: &mut dyn ProtocolCaller) -> VmResult { let value = vm_try!(caller.call_protocol_fn(Protocol::INTO_ITER, self, &mut ())); - Iterator::from_value(value) + VmResult::Ok(Iterator::new(value)) } /// Retrieves a human readable type name for the current value. @@ -1241,16 +1238,6 @@ impl Value { into_generator, } - into! { - /// Coerce into a [`Iterator`]. - Iterator(Iterator), - into_iterator_ref, - into_iterator_mut, - borrow_iterator_ref, - borrow_iterator_mut, - into_iterator, - } - into! { /// Coerce into a [`Format`]. Format(Format), @@ -2148,6 +2135,48 @@ impl Value { ValueRepr::Empty => Err(AccessError::empty()), } } + + pub(crate) fn protocol_into_iter(&self) -> VmResult { + EnvProtocolCaller.call_protocol_fn(Protocol::INTO_ITER, self.clone(), &mut ()) + } + + pub(crate) fn protocol_next(&self) -> VmResult> { + let value = + vm_try!(EnvProtocolCaller.call_protocol_fn(Protocol::NEXT, self.clone(), &mut ())); + + FromValue::from_value(value) + } + + pub(crate) fn protocol_next_back(&self) -> VmResult> { + let value = + vm_try!(EnvProtocolCaller.call_protocol_fn(Protocol::NEXT_BACK, self.clone(), &mut ())); + + FromValue::from_value(value) + } + + pub(crate) fn protocol_nth_back(&self, n: usize) -> VmResult> { + let value = vm_try!(EnvProtocolCaller.call_protocol_fn( + Protocol::NTH_BACK, + self.clone(), + &mut Some((n,)) + )); + + FromValue::from_value(value) + } + + pub(crate) fn protocol_len(&self) -> VmResult { + let value = + vm_try!(EnvProtocolCaller.call_protocol_fn(Protocol::LEN, self.clone(), &mut ())); + + FromValue::from_value(value) + } + + pub(crate) fn protocol_size_hint(&self) -> VmResult<(usize, Option)> { + let value = + vm_try!(EnvProtocolCaller.call_protocol_fn(Protocol::SIZE_HINT, self.clone(), &mut ())); + + FromValue::from_value(value) + } } impl fmt::Debug for Value { @@ -2288,7 +2317,6 @@ impl_from! { Bytes => Bytes, ControlFlow => ControlFlow, Function => Function, - Iterator => Iterator, GeneratorState => GeneratorState, Vec => Vec, EmptyStruct => EmptyStruct, @@ -2440,8 +2468,6 @@ pub(crate) enum ValueKind { Function(Function), /// A value being formatted. Format(Format), - /// An iterator. - Iterator(Iterator), /// An opaque value that can be downcasted. Any(AnyObj), } @@ -2491,7 +2517,6 @@ impl ValueKind { ValueKind::Result(..) => TypeInfo::StaticType(crate::runtime::static_type::RESULT), ValueKind::Function(..) => TypeInfo::StaticType(crate::runtime::static_type::FUNCTION), ValueKind::Format(..) => TypeInfo::StaticType(crate::runtime::static_type::FORMAT), - ValueKind::Iterator(..) => TypeInfo::StaticType(crate::runtime::static_type::ITERATOR), ValueKind::EmptyStruct(empty) => empty.type_info(), ValueKind::TupleStruct(tuple) => tuple.type_info(), ValueKind::Struct(object) => object.type_info(), @@ -2534,7 +2559,6 @@ impl ValueKind { ValueKind::Option(..) => crate::runtime::static_type::OPTION.hash, ValueKind::Function(..) => crate::runtime::static_type::FUNCTION.hash, ValueKind::Format(..) => crate::runtime::static_type::FORMAT.hash, - ValueKind::Iterator(..) => crate::runtime::static_type::ITERATOR.hash, ValueKind::EmptyStruct(empty) => empty.rtti.hash, ValueKind::TupleStruct(tuple) => tuple.rtti.hash, ValueKind::Struct(object) => object.rtti.hash, diff --git a/crates/rune/src/runtime/value/serde.rs b/crates/rune/src/runtime/value/serde.rs index 000d79c6e..eea1ab3b1 100644 --- a/crates/rune/src/runtime/value/serde.rs +++ b/crates/rune/src/runtime/value/serde.rs @@ -81,7 +81,6 @@ impl ser::Serialize for Value { ValueKind::Format(..) => { Err(ser::Error::custom("cannot serialize format specifications")) } - ValueKind::Iterator(..) => Err(ser::Error::custom("cannot serialize iterators")), ValueKind::RangeFrom(..) => { Err(ser::Error::custom("cannot serialize `start..` ranges")) } diff --git a/crates/rune/src/runtime/vec.rs b/crates/rune/src/runtime/vec.rs index a3230a68e..d7c5942f2 100644 --- a/crates/rune/src/runtime/vec.rs +++ b/crates/rune/src/runtime/vec.rs @@ -1,5 +1,3 @@ -mod iter; - use core::cmp; use core::cmp::Ordering; use core::fmt; @@ -11,16 +9,15 @@ use crate as rune; use crate::alloc; use crate::alloc::fmt::TryWrite; use crate::alloc::prelude::*; +use crate::runtime::slice::Iter; #[cfg(feature = "alloc")] use crate::runtime::Hasher; use crate::runtime::{ - Formatter, FromValue, Iterator, ProtocolCaller, RawRef, Ref, ToValue, UnsafeToRef, Value, - ValueKind, VmErrorKind, VmResult, + Formatter, FromValue, ProtocolCaller, RawRef, Ref, ToValue, UnsafeToRef, Value, ValueKind, + VmErrorKind, VmResult, }; use crate::Any; -use self::iter::Iter; - /// Struct representing a dynamic vector. /// /// # Examples @@ -80,7 +77,7 @@ impl Vec { }) } - /// Convert into inner std vector. + /// Convert into inner rune alloc vector. pub fn into_inner(self) -> alloc::Vec { self.inner } @@ -207,9 +204,20 @@ impl Vec { VmResult::Ok(()) } - /// Convert into a rune iterator. - pub fn iter_ref(this: Ref<[Value]>) -> Iterator { - Iterator::from_double_ended("std::slice::Iter", Iter::new(this)) + /// Iterate over the vector. + /// + /// # Examples + /// + /// ```rune + /// let vec = [1, 2, 3, 4]; + /// let it = vec.iter(); + /// + /// assert_eq!(it.next(), Some(1)); + /// assert_eq!(it.next_back(), Some(4)); + /// ``` + #[rune::function(keep, path = Self::iter)] + pub fn rune_iter(this: Ref) -> Iter { + Iter::new(Ref::map(this, |vec| &**vec)) } /// Access the inner values as a slice. diff --git a/crates/rune/src/shared/caller.rs b/crates/rune/src/shared/caller.rs new file mode 100644 index 000000000..1a03e1e8e --- /dev/null +++ b/crates/rune/src/shared/caller.rs @@ -0,0 +1,66 @@ +use core::marker::PhantomData; + +use rust_alloc::sync::Arc; + +use crate::runtime::{FixedArgs, FunctionHandler, InstAddress, Output, VmResult}; +use crate::FromValue; + +/// Helper struct to conveniently call native functions. +/// +/// Note: This can only be used with functions that take at least one argument. +/// Otherwise it will panic. +#[derive(Clone)] +pub(crate) struct Caller { + handler: Arc, + _marker: PhantomData, +} + +impl Caller +where + T: FromValue, +{ + /// Construct a new caller helper + pub(crate) fn new(handler: Arc) -> Self { + Self { + handler, + _marker: PhantomData, + } + } + + /// Modify the return value of the caller. + pub(crate) fn with_return(&self) -> Caller + where + U: FromValue, + { + Caller { + handler: self.handler.clone(), + _marker: PhantomData, + } + } + + /// Perform a call. + pub(crate) fn call(&self, args: impl FixedArgs) -> VmResult { + const { + assert!(N > 0, "Must be used with non-zero arguments"); + } + + let mut args = vm_try!(args.into_array()); + + vm_try!((self.handler)( + &mut args, + InstAddress::ZERO, + N, + Output::keep(0) + )); + + let Some(value) = args.into_iter().next() else { + unreachable!(); + }; + + T::from_value(value) + } +} + +// SAFETY: The marker doesn't matter. +unsafe impl Send for Caller {} +unsafe impl Sync for Caller {} diff --git a/crates/rune/src/shared/mod.rs b/crates/rune/src/shared/mod.rs index c34cd4645..d3c34699e 100644 --- a/crates/rune/src/shared/mod.rs +++ b/crates/rune/src/shared/mod.rs @@ -1,9 +1,11 @@ mod assert_send; +mod caller; mod consts; mod fixed_vec; mod gen; pub(crate) use self::assert_send::AssertSend; +pub(crate) use self::caller::Caller; pub(crate) use self::consts::Consts; pub(crate) use self::fixed_vec::{CapacityError, FixedVec}; pub(crate) use self::gen::Gen; diff --git a/crates/rune/src/tests.rs b/crates/rune/src/tests.rs index 979cb14af..251f5b401 100644 --- a/crates/rune/src/tests.rs +++ b/crates/rune/src/tests.rs @@ -15,8 +15,8 @@ pub(crate) mod prelude { pub(crate) use crate::module::InstallWith; pub(crate) use crate::parse; pub(crate) use crate::runtime::{ - self, AnyTypeInfo, Bytes, Formatter, Function, InstAddress, MaybeTypeOf, Mut, Object, - Output, OwnedTuple, Protocol, RawRef, RawStr, Ref, Stack, Tuple, TypeInfo, TypeOf, + self, AnyTypeInfo, Bytes, CoreTypeOf, Formatter, Function, InstAddress, MaybeTypeOf, Mut, + Object, Output, OwnedTuple, Protocol, RawRef, RawStr, Ref, Stack, Tuple, TypeInfo, TypeOf, UnsafeToRef, ValueKind, VecTuple, VmErrorKind, VmResult, }; pub(crate) use crate::support::Result; diff --git a/crates/rune/src/tests/bug_344.rs b/crates/rune/src/tests/bug_344.rs index 468eb3eba..b667deb83 100644 --- a/crates/rune/src/tests/bug_344.rs +++ b/crates/rune/src/tests/bug_344.rs @@ -58,7 +58,7 @@ fn bug_344_inst_fn() -> Result<()> { context.install(module)?; let runtime = context.runtime()?; - let hash = Hash::associated_function(::type_hash(), "function"); + let hash = Hash::associated_function(GuardCheck::type_hash(), "function"); let function = runtime.function(hash).expect("expect function"); @@ -120,7 +120,7 @@ fn bug_344_async_inst_fn() -> Result<()> { context.install(module)?; let runtime = context.runtime()?; - let hash = Hash::associated_function(::type_hash(), "function"); + let hash = Hash::associated_function(GuardCheck::type_hash(), "function"); let function = runtime.function(hash).expect("expect function"); @@ -170,27 +170,24 @@ impl GuardCheck { } } -impl Any for GuardCheck { - fn type_hash() -> Hash { - rune_macros::hash!(GuardCheck) - } -} +impl Any for GuardCheck {} impl Named for GuardCheck { const BASE_NAME: RawStr = RawStr::from_str("GuardCheck"); } -impl TypeOf for GuardCheck { - #[inline] +impl CoreTypeOf for GuardCheck { fn type_hash() -> Hash { - ::type_hash() + rune_macros::hash!(GuardCheck) } +} +impl TypeOf for GuardCheck { #[inline] fn type_info() -> TypeInfo { TypeInfo::Any(AnyTypeInfo::__private_new( - ::BASE_NAME, - ::type_hash(), + Self::BASE_NAME, + Self::type_hash(), )) } } @@ -198,7 +195,7 @@ impl TypeOf for GuardCheck { impl MaybeTypeOf for GuardCheck { #[inline] fn maybe_type_of() -> alloc::Result { - Ok(meta::DocType::new(::type_hash())) + Ok(meta::DocType::new(Self::type_hash())) } } diff --git a/crates/rune/src/tests/external_generic.rs b/crates/rune/src/tests/external_generic.rs index a68f6b072..aa9f6c6c3 100644 --- a/crates/rune/src/tests/external_generic.rs +++ b/crates/rune/src/tests/external_generic.rs @@ -106,7 +106,7 @@ fn test_generic() -> Result<()> { $( let value = vm.call([stringify!($function_ty)], ()).with_context(|| anyhow!("{}: {}: Working call", stringify!($ty), stringify!($function_ty)))?; let value: Type = rune::from_value(value).with_context(|| anyhow!("{}: {}: Output value", stringify!($ty), stringify!($function_ty)))?; - assert_eq!( as Any>::type_hash(), value.into_hash()); + assert_eq!(Generic::<$ty>::type_hash(), value.into_hash()); )* }; } diff --git a/crates/rune/src/tests/generics.rs b/crates/rune/src/tests/generics.rs index b14ed0d9a..7d3516c91 100644 --- a/crates/rune/src/tests/generics.rs +++ b/crates/rune/src/tests/generics.rs @@ -13,21 +13,21 @@ fn test_collect_vec() { assert_eq!(values, vec![4, 3, 2, 1]); let values: Vec = rune! { - use std::iter::Iterator; + use std::slice::Iter; pub fn main() { let it = [4, 3, 2, 1].iter(); - Iterator::collect::(it) + Iter::collect::(it) } }; assert_eq!(values, vec![4, 3, 2, 1]); let values: Vec = rune! { - use std::iter::Iterator; + use std::slice::Iter; pub fn main() { let it = [4, 3, 2, 1].iter(); - let c = Iterator::collect::; + let c = Iter::collect::; c(it) } }; @@ -53,11 +53,11 @@ fn test_collect_object() { assert_eq!(values, expected); let values: HashMap = rune! { - use std::iter::Iterator; + use std::slice::Iter; pub fn main() { let it = [("a", 4), ("b", 3), ("c", 2), ("d", 1)].iter(); - Iterator::collect::(it) + Iter::collect::(it) } }; let expected = [ @@ -71,11 +71,11 @@ fn test_collect_object() { assert_eq!(values, expected); let values: HashMap = rune! { - use std::iter::Iterator; + use std::slice::Iter; pub fn main() { let it = [("a", 4), ("b", 3), ("c", 2), ("d", 1)].iter(); - let c = Iterator::collect::; + let c = Iter::collect::; c(it) } }; diff --git a/crates/rune/src/tests/iterator.rs b/crates/rune/src/tests/iterator.rs index 599bd7043..273593ff6 100644 --- a/crates/rune/src/tests/iterator.rs +++ b/crates/rune/src/tests/iterator.rs @@ -62,8 +62,7 @@ fn test_next_back() { fn test_object_rev_error() { assert_vm_error!( r#"pub fn main() { #{}.iter().rev() }"#, - Panic { reason } => { - assert_eq!(reason.to_string(), "`std::object::Iter` is not a double-ended iterator"); + MissingInstanceFunction { hash: rune::hash!(::std::object::Iter.rev), .. } => { } ); } diff --git a/crates/rune/src/tests/vm_generators.rs b/crates/rune/src/tests/vm_generators.rs index 08355f291..c6f88b564 100644 --- a/crates/rune/src/tests/vm_generators.rs +++ b/crates/rune/src/tests/vm_generators.rs @@ -22,7 +22,7 @@ fn test_simple_generator() { #[test] fn test_resume() { let out: i64 = rune! { - use std::ops::GeneratorState; + use std::ops::generator::GeneratorState; fn foo() { let a = yield 1; let b = yield a; b } diff --git a/crates/rune/src/tests/vm_streams.rs b/crates/rune/src/tests/vm_streams.rs index f45517257..e2f580033 100644 --- a/crates/rune/src/tests/vm_streams.rs +++ b/crates/rune/src/tests/vm_streams.rs @@ -32,7 +32,7 @@ fn test_simple_stream() { #[test] fn test_resume() { let out: i64 = rune! { - use std::ops::GeneratorState; + use std::ops::generator::GeneratorState; async fn foo() { let a = yield 1; let b = yield a; b } diff --git a/scripts/generator_resume.rn b/scripts/generator_resume.rn index cd82a313d..20dcdfdbe 100644 --- a/scripts/generator_resume.rn +++ b/scripts/generator_resume.rn @@ -1,4 +1,4 @@ -use std::ops::GeneratorState; +use std::ops::generator::GeneratorState; fn foo() { let a = yield 1; diff --git a/tools/function_traits_permute.py b/tools/function_traits_permute.py index b246c8fdf..4d45ed893 100644 --- a/tools/function_traits_permute.py +++ b/tools/function_traits_permute.py @@ -12,7 +12,7 @@ this = pathlib.Path(__file__) name = this.parts[-1] -out = this.parent.parent.joinpath("crates/rune/src/module/function_traits/macros.rs") +out = this.parent.parent.joinpath("crates/rune/src/function/macros.rs") with out.open(mode = 'bw') as fd: def pr(text):