This was originally intended to follow-up my post about generalized coroutines with a proposal for “Yield Closures”. However, almost everything in it is better presented in the MCP-49 issue. I’m just going to leave a couple of examples here as a kind of appendix to that issue.
Here is a relatively simple lisp repl:
// Will run until all whitespace chars are consumed.
fn whitespace(c: char) -> Poll<()> {
match c.is_whitespace() {
true => Pending,
false => Ready(()),
}
}
// Will run until all chars in an expression are consumed.
// Returns the value of the expression.
fn expression() -> impl FnMut(char) -> Poll<Result<u32, Invalid>> {
|c| {
poll!(whitespace, c);
if c == '(' {
yield Pending; // Consume (
poll!(whitespace, c); // Consume whitespace
let (mut value, op): (u32, fn(u32, u32) -> u32) = match c {
'+' => (0, |a, b| a + b),
'*' => (1, |a, b| a * b),
'|' => (0, |a, b| a | b),
'&' => (!0, |a, b| a & b),
_ => return Ready(Err(Invalid::Operator)),
};
yield Pending; // Consume operator
if !c.is_whitespace() && c != ')' {
return Ready(Err(Invalid::Spacing));
}
poll!(whitespace, c); // Consume whitespace
let mut args = expression();
while c != ')' {
value = op(value, poll!(args, c)?); // Consume expression
poll!(whitespace, c); // Consume whitespace
}
yield Pending; // Consume )
Ready(Ok(value))
} else if c.is_digit() {
let mut value = 0;
while c.is_digit() {
value *= 10;
value += char::from_digit(c).ok_or(Invalid::Number)?;
yield Pending; // Consume digit
}
Ready(Ok(value))
} else {
Ready(Err(Invalid::Expression))
}
}
}
// REPL
fn main() -> Result<(), Error> {
let mut eval = expression();
for line in stdin().lock()?.lines() {
for c in line?.chars() {
if let Ready(x) = eval(c) {
println!("= {}", x?);
}
}
}
Ok(())
}
Eventually we could add a .await(...)
syntax which polls FnPin::call_pin
with the given expressions as arguments. It would be internally very similar to
.await
, which polls Future::poll
with the implicit async context. The key
difference being, .await(..)
can be used anywhere yield
is permitted. This
is useful for interacting with arbitrary sorts of poll functions & closures:
move mut |ctx: &mut Context| {
let mut buffer = [0u8; 4096];
pin_mut!(read);
loop {
let n = AsyncRead::poll_read.await(read.as_mut(), ctx, &mut buffer)?;
if n == 0 {
return Ready(None);
}
for &byte in buffer.iter().take(n) {
yield Ready(Some(Ok(byte)));
}
}
}