Skip to content

Commit 5e86995

Browse files
committed
Add support for captured arguments from macros
1 parent 1e05a36 commit 5e86995

File tree

10 files changed

+181
-79
lines changed

10 files changed

+181
-79
lines changed

book/src/hot_reloading.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ A typical way to accomplish this is to watch a scripts directory using the
1111
to the directory are detected. See the [`hot_reloading` example] and in
1212
particular the [`PathReloader`] type.
1313

14-
```
14+
```rust
1515
{{#include ../../examples/examples/hot_reloading.rs}}
1616
```
1717

book/src/multithreading.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ such as Tokio. This is achieved by ensuring that all captured arguments are
3636
[`ConstValue`]'s, which in contrast to [`Value`]'s are guaranteed to be
3737
thread-safe:
3838

39-
```
39+
```rust
4040
{{#include ../../examples/examples/tokio_spawn.rs}}
4141
```
4242

4343
Finally [`Function::into_sync`] exists to coerce a function into a
44-
[`SyncFunction], which is a thread-safe variant of a regular [`Function`]. This
44+
[`SyncFunction`], which is a thread-safe variant of a regular [`Function`]. This
4545
is a fallible operation since all values which are captured in the function-type
4646
in case its a closure has to be coerced to [`ConstValue`]. If this is not the
4747
case, the conversion will fail.

crates/rune/src/compile/context.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ impl Context {
152152
this.install(crate::modules::fmt::module()?)?;
153153
this.install(crate::modules::future::module()?)?;
154154
this.install(crate::modules::i64::module()?)?;
155-
#[cfg(feature = "std")]
156155
this.install(crate::modules::io::module(stdio)?)?;
157156
this.install(crate::modules::iter::module()?)?;
158157
this.install(crate::modules::macros::module()?)?;

crates/rune/src/lib.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,17 @@
2929
//! * Runs a compact representation of the language on top of an efficient
3030
//! [stack-based virtual machine][support-virtual-machine].
3131
//! * Clean [Rust integration 💻][support-rust-integration].
32+
//! * [Multithreaded 📖][support-multithreading] execution.
33+
//! * [Hot reloading 📖][support-hot-reloading].
3234
//! * Memory safe through [reference counting 📖][support-reference-counted].
33-
//! * [Awesome macros 📖][support-macros].
34-
//! * [Template literals 📖][support-templates].
35-
//! * [Try operators 📖][support-try].
36-
//! * [Pattern matching 📖][support-patterns].
35+
//! * [Awesome macros 📖][support-macros] and [Template literals 📖][support-templates].
36+
//! * [Try operators 📖][support-try] and [Pattern matching 📖][support-patterns].
3737
//! * [Structs and enums 📖][support-structs] with associated data and
3838
//! functions.
39-
//! * Dynamic [vectors 📖][support-dynamic-vectors], [objects
40-
//! 📖][support-anon-objects], and [tuples 📖][support-anon-tuples] with
39+
//! * Dynamic containers like [vectors 📖][support-dynamic-vectors], [objects
40+
//! 📖][support-anon-objects], and [tuples 📖][support-anon-tuples] all with
4141
//! out-of-the-box [serde support 💻][support-serde].
42-
//! * First-class [async support 📖][support-async].
43-
//! * [Generators 📖][support-generators].
42+
//! * First-class [async support 📖][support-async] with [Generators 📖][support-generators].
4443
//! * Dynamic [instance functions 📖][support-instance-functions].
4544
//! * [Stack isolation 📖][support-stack-isolation] between function calls.
4645
//!
@@ -114,8 +113,10 @@
114113
//! [support-async]: https://rune-rs.github.io/book/async.html
115114
//! [support-dynamic-vectors]: https://rune-rs.github.io/book/vectors.html
116115
//! [support-generators]: https://rune-rs.github.io/book/generators.html
116+
//! [support-hot-reloading]: https://rune-rs.github.io/book/hot_reloading.html
117117
//! [support-instance-functions]: https://rune-rs.github.io/book/instance_functions.html
118118
//! [support-macros]: https://rune-rs.github.io/book/macros.html
119+
//! [support-multithreading]: https://rune-rs.github.io/book/multithreading.html
119120
//! [support-patterns]: https://rune-rs.github.io/book/pattern_matching.html
120121
//! [support-reference-counted]: https://rune-rs.github.io/book/variables.html
121122
//! [support-rust-integration]: https://github.com/rune-rs/rune/tree/main/crates/rune-modules

crates/rune/src/macros/format_args.rs

Lines changed: 89 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::alloc::prelude::*;
55
use crate::alloc::{self, BTreeMap, BTreeSet, Box, HashMap, String, Vec};
66
use crate::ast::{self, Span, Spanned};
77
use crate::compile::{self, WithSpan};
8-
use crate::macros::{quote, MacroContext, Quote};
8+
use crate::macros::{quote, MacroContext, Quote, ToTokens, TokenStream};
99
use crate::parse::{Parse, Parser, Peek, Peeker};
1010
use crate::runtime::format;
1111
use crate::runtime::ValueKind;
@@ -49,14 +49,11 @@ impl FormatArgs {
4949
}
5050
}
5151

52-
let format = match format.take_kind().with_span(&self.format)? {
53-
ValueKind::String(string) => string,
54-
_ => {
55-
return Err(compile::Error::msg(
56-
&self.format,
57-
"format argument must be a string",
58-
));
59-
}
52+
let ValueKind::String(format) = format.take_kind().with_span(&self.format)? else {
53+
return Err(compile::Error::msg(
54+
&self.format,
55+
"format argument must be a string",
56+
));
6057
};
6158

6259
let mut unused_pos = (0..pos.len()).try_collect::<BTreeSet<_>>()?;
@@ -65,19 +62,19 @@ impl FormatArgs {
6562
.map(|(key, n)| Ok::<_, alloc::Error>((key.try_clone()?, n.span())))
6663
.try_collect::<alloc::Result<BTreeMap<_, _>>>()??;
6764

68-
let expanded = match expand_format_spec(
65+
let result = expand_format_spec(
6966
cx,
7067
self.format.span(),
7168
&format,
7269
&pos,
7370
&mut unused_pos,
7471
&named,
7572
&mut unused_named,
76-
) {
73+
);
74+
75+
let expanded = match result {
7776
Ok(expanded) => expanded,
78-
Err(message) => {
79-
return Err(compile::Error::msg(self.format.span(), message));
80-
}
77+
Err(message) => return Err(compile::Error::msg(self.format.span(), message)),
8178
};
8279

8380
if let Some(expr) = unused_pos.into_iter().flat_map(|n| pos.get(n)).next() {
@@ -174,18 +171,27 @@ fn expand_format_spec<'a>(
174171
let mut name = String::new();
175172
let mut width = String::new();
176173
let mut precision = String::new();
177-
let mut buf = String::new();
178174

175+
let mut buf = String::new();
179176
let mut components = Vec::new();
180177
let mut count = 0;
178+
let mut start = Some(0);
181179

182-
while let Some(value) = iter.next() {
183-
match value {
180+
while let Some((at, a, b)) = iter.next() {
181+
match (a, b) {
184182
('}', '}') => {
183+
if let Some(start) = start.take() {
184+
buf.try_push_str(&input[start..at])?;
185+
}
186+
185187
buf.try_push('}')?;
186188
iter.next();
187189
}
188190
('{', '{') => {
191+
if let Some(start) = start.take() {
192+
buf.try_push_str(&input[start..at])?;
193+
}
194+
189195
buf.try_push('{')?;
190196
iter.next();
191197
}
@@ -196,8 +202,12 @@ fn expand_format_spec<'a>(
196202
));
197203
}
198204
('{', _) => {
205+
if let Some(start) = start.take() {
206+
buf.try_push_str(&input[start..at])?;
207+
}
208+
199209
if !buf.is_empty() {
200-
components.try_push(C::Literal(buf.try_clone()?.try_into_boxed_str()?))?;
210+
components.try_push(C::Literal(Box::try_from(&buf[..])?))?;
201211
buf.clear();
202212
}
203213

@@ -215,14 +225,20 @@ fn expand_format_spec<'a>(
215225
unused_named,
216226
)?)?;
217227
}
218-
(a, _) => {
219-
buf.try_push(a)?;
228+
_ => {
229+
if start.is_none() {
230+
start = Some(at);
231+
}
220232
}
221233
}
222234
}
223235

236+
if let Some(start) = start.take() {
237+
buf.try_push_str(&input[start..])?;
238+
}
239+
224240
if !buf.is_empty() {
225-
components.try_push(C::Literal(buf.try_clone()?.try_into_boxed_str()?))?;
241+
components.try_push(C::Literal(Box::try_from(&buf[..])?))?;
226242
buf.clear();
227243
}
228244

@@ -235,7 +251,7 @@ fn expand_format_spec<'a>(
235251
for c in components {
236252
match c {
237253
C::Literal(literal) => {
238-
let lit = cx.lit(&*literal)?;
254+
let lit = cx.lit(literal.as_ref())?;
239255
args.try_push(quote!(#lit))?;
240256
}
241257
C::Format {
@@ -314,10 +330,28 @@ fn expand_format_spec<'a>(
314330
#[builtin] template!(#(args),*)
315331
});
316332

333+
enum ExprOrIdent<'a> {
334+
Expr(&'a ast::Expr),
335+
Ident(ast::Ident),
336+
}
337+
338+
impl ToTokens for ExprOrIdent<'_> {
339+
fn to_tokens(
340+
&self,
341+
cx: &mut MacroContext<'_, '_, '_>,
342+
stream: &mut TokenStream,
343+
) -> alloc::Result<()> {
344+
match self {
345+
Self::Expr(expr) => expr.to_tokens(cx, stream),
346+
Self::Ident(ident) => ident.to_tokens(cx, stream),
347+
}
348+
}
349+
}
350+
317351
enum C<'a> {
318352
Literal(Box<str>),
319353
Format {
320-
expr: &'a ast::Expr,
354+
expr: ExprOrIdent<'a>,
321355
fill: Option<char>,
322356
align: Option<format::Alignment>,
323357
width: Option<usize>,
@@ -384,11 +418,8 @@ fn expand_format_spec<'a>(
384418
let mut mode = Mode::Start;
385419

386420
loop {
387-
let (a, b) = match iter.current() {
388-
Some(item) => item,
389-
_ => {
390-
return Err(compile::Error::msg(span, "unexpected end of format string"));
391-
}
421+
let Some((_, a, b)) = iter.current() else {
422+
return Err(compile::Error::msg(span, "unexpected end of format string"));
392423
};
393424

394425
match mode {
@@ -577,7 +608,20 @@ fn expand_format_spec<'a>(
577608
None
578609
};
579610

580-
let expr = if !name.is_empty() {
611+
let expr = 'expr: {
612+
if name.is_empty() {
613+
let Some(expr) = pos.get(*count) else {
614+
return Err(compile::Error::msg(
615+
span,
616+
format!("missing positional argument #{count}"),
617+
));
618+
};
619+
620+
unused_pos.remove(count);
621+
*count += 1;
622+
break 'expr ExprOrIdent::Expr(expr);
623+
};
624+
581625
if let Ok(n) = str::parse::<usize>(name) {
582626
let expr = match pos.get(n) {
583627
Some(expr) => *expr,
@@ -590,35 +634,17 @@ fn expand_format_spec<'a>(
590634
};
591635

592636
unused_pos.remove(&n);
593-
expr
594-
} else {
595-
let expr = match named.get(name.as_str()) {
596-
Some(n) => &n.expr,
597-
None => {
598-
return Err(compile::Error::msg(
599-
span,
600-
format!("missing named argument `{}`", name),
601-
));
602-
}
603-
};
637+
break 'expr ExprOrIdent::Expr(expr);
638+
}
604639

640+
if let Some(n) = named.get(name.as_str()) {
605641
unused_named.remove(name.as_str());
606-
expr
642+
break 'expr ExprOrIdent::Expr(&n.expr);
607643
}
608-
} else {
609-
let expr = match pos.get(*count) {
610-
Some(expr) => *expr,
611-
None => {
612-
return Err(compile::Error::msg(
613-
span,
614-
format!("missing positional argument #{}", count),
615-
));
616-
}
617-
};
618644

619-
unused_pos.remove(count);
620-
*count += 1;
621-
expr
645+
let mut ident = cx.ident(name.as_str())?;
646+
ident.span = span;
647+
ExprOrIdent::Ident(ident)
622648
};
623649

624650
let width = if !width.is_empty() {
@@ -648,30 +674,28 @@ fn expand_format_spec<'a>(
648674
}
649675

650676
struct Iter<'a> {
651-
iter: str::Chars<'a>,
652-
a: Option<char>,
653-
b: Option<char>,
677+
iter: str::CharIndices<'a>,
678+
a: Option<(usize, char)>,
679+
b: Option<(usize, char)>,
654680
}
655681

656682
impl<'a> Iter<'a> {
657683
fn new(input: &'a str) -> Self {
658-
let mut iter = input.chars();
659-
684+
let mut iter = input.char_indices();
660685
let a = iter.next();
661686
let b = iter.next();
662-
663687
Self { iter, a, b }
664688
}
665689

666-
fn current(&self) -> Option<(char, char)> {
667-
let a = self.a?;
668-
let b = self.b.unwrap_or_default();
669-
Some((a, b))
690+
fn current(&self) -> Option<(usize, char, char)> {
691+
let (pos, a) = self.a?;
692+
let (_, b) = self.b.unwrap_or_default();
693+
Some((pos, a, b))
670694
}
671695
}
672696

673697
impl Iterator for Iter<'_> {
674-
type Item = (char, char);
698+
type Item = (usize, char, char);
675699

676700
fn next(&mut self) -> Option<Self::Item> {
677701
let value = self.current()?;

crates/rune/src/macros/token_stream.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ pub trait ToTokens {
125125
/// Turn the current item into tokens.
126126
fn to_tokens(
127127
&self,
128-
context: &mut MacroContext<'_, '_, '_>,
128+
cx: &mut MacroContext<'_, '_, '_>,
129129
stream: &mut TokenStream,
130130
) -> alloc::Result<()>;
131131
}

crates/rune/src/modules.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ pub mod future;
2121
pub mod generator;
2222
pub mod hash;
2323
pub mod i64;
24-
#[cfg(feature = "std")]
2524
pub mod io;
2625
pub mod iter;
2726
pub mod macros;

0 commit comments

Comments
 (0)