Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(forth3): return interpreter actions from builtins #65

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

hawkw
Copy link
Contributor

@hawkw hawkw commented Jun 9, 2023

This branch changes the forth3 API to prepare for Forth-driven IO. In
particular, builtin words and VM execution steps are changed from
returning a Ok(()) branch when the builtin/execution step succeeds to
returning a new InterpretAction type in the Ok branch, which
determines what action the host should perform next.

InterpretAction is an enum which currently has three variants:

  • InterpretAction::Done, which has the same semantics that returning
    Ok(()) currently has: if returned from a builtin, it indicates that
    the VM should pop the current callstack frame and keep executing, and
    if returned by the VM to the host, it indicates that the VM's
    callstack is empty.
  • InterpretAction::Continue, which replaces the
    Error::PendingCallAgain variant (it seemed more morally correct for
    this to be an Ok return rather than an Err), and indicates that
    the VM should continue executing what's currently on the stack when
    returned from a builtin
  • InterpretAction::AcceptInput, which indicates that the VM needs to
    wait for the host to fill its input buffer.

In the future, InterpretAction::AcceptInput will be used to provide an
improved abstraction for communicating to the host that the VM is
requesting IO. We may also add a variant indicating that the VM needs
the host to drain its output buffer. When we add implementations of
Forth words such as quit, which fill the input buffer, they will
return the AcceptInput variant, allowing us to request IO from
builtins provided by forth3 without requiring them to actually do
the IO. This lets us avoid making the entire VM generic over some Read
and Write traits (or AsyncRead/AsyncWrite traits), which don't
exist in no-std, by temporarily breaking out of a VM's run loop and
returning to the host when I/O is needed.

@netlify
Copy link

netlify bot commented Jun 9, 2023

Deploy Preview for merry-scone-cc7a60 ready!

Name Link
🔨 Latest commit 538edcc
🔍 Latest deploy log https://app.netlify.com/sites/merry-scone-cc7a60/deploys/64834aae3344ef00089188fd
😎 Deploy Preview https://deploy-preview-65--merry-scone-cc7a60.netlify.app/doc/kernel/fmt/trait.debug
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@hawkw hawkw requested a review from jamesmunns June 9, 2023 15:44
@hawkw hawkw marked this pull request as ready for review June 9, 2023 15:44
Execute,
#[derive(Debug, Copy, Clone, Eq, PartialEq)]

pub enum InterpretAction {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not particularly attached to the name InterpretAction, but I couldn't think of something better --- @jamesmunns, if you want to bikeshed this, I'm all ears!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a very strong opinion on the name, though it would probably be good to add doc comments here that explain what each choice means.

For AcceptInput (which I think means "block until input buffer is filled"), do we want to make this something like:

pub enum InterpretAction {
    Done,
    Continue,
    BlockedOn(BlockReason),
}

pub enum BlockReason {
    AcceptInput,
    OutputFull,
    // ...
    Custom(u32), // User defined blocking reason?
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whoops, I meant to add docs to this --- thanks for calling that out!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could add a nested enum of blocking reasons, but the one you're proposing does make the return value substantially larger: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=2a13f71d45fd5a02872bd972c1ecba58

worrying about enum size here may be a bit of a premature optimization, but it is the return value of basically every interpreter step, so we pass these enums around kind of a lot --- and they're already inside of a Result<InterpretAction, Error>, which adds a couple more layers of discriminants...we probably want to ensure that the return value of these functions fits in a register, ideally?

This branch changes the `forth3` API to prepare for Forth-driven IO. In
particular, builtin words and VM execution steps are changed from
returning a `Ok(())` branch when the builtin/execution step succeeds to
returning a new `InterpretAction` type in the `Ok` branch, which
determines what action the host should perform next.

`InterpretAction` is an enum which currently has three variants:

- `InterpretAction::Done`, which has the same semantics that returning
  `Ok(())` currently has: if returned from a builtin, it indicates that
  the VM should pop the current callstack frame and keep executing, and
  if returned by the VM to the host, it indicates that the VM's
  callstack is empty.
- `InterpretAction::Continue`, which replaces the
  `Error::PendingCallAgain` variant (it seemed more morally correct for
  this to be an `Ok` return rather than an `Err`), and indicates that
  the VM should continue executing what's currently on the stack when
  returned from a builtin
- `InterpretAction::AcceptInput`, which indicates that the VM needs to
  wait for the host to fill its input buffer.

In the future, `InterpretAction::AcceptInput` will be used to provide an
improved abstraction for communicating to the host that the VM is
requesting IO. We may also add a variant indicating that the VM needs
the host to drain its output buffer. When we add implementations of
Forth words such as `quit`, which fill the input buffer, they will
return the `AcceptInput` variant, allowing us to request IO from
builtins provided by `forth3` without requiring them to actually *do*
the IO. This lets us avoid making the entire VM generic over some `Read`
and `Write` traits (or `AsyncRead`/`AsyncWrite` traits), which don't
exist in no-std, by temporarily breaking out of a VM's run loop and
returning to the host when I/O is needed.
@hawkw hawkw force-pushed the eliza/interpretaction branch from b9acd48 to 538edcc Compare June 9, 2023 15:52
Copy link
Contributor

@jamesmunns jamesmunns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good to me. Some thoughts on the blocking enum, and I'd like to have docs for InterpretAction, but in principle I'm very +1 for this!

@jamesmunns
Copy link
Contributor

@hawkw does it make sense to get this merged sometime?

@hawkw hawkw self-assigned this Sep 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants