Introducing The Calypso Chronicles
Tags: calypso, rust, devjournal, langdev
Published: 2021-03-02 02:43:52

Edit: Apparently this got featured in TWIR! Thanks editors! :)
If you don't already know what Calypso is, I'd recommend reading the post I wrote about it a little bit after when I wrote this:
{%post thepuzzlemaker/what-is-calypso-42o7 %}

I recently found this site and actually have started to consider using it. I'm probably going to use this as a new version of [my blog](https://blog.thepuzzlemaker.info) as I've been meaning to use something else for a little while.

Anyway, I'm going to start a new, (semi-)weekly series called "The Calypso Chronicles" about my status on Calypso, both how it's going and my plans for the future. This is mostly just for myself (though feel free to discuss anything here if you want to!).

It will be split into two sections: one for Calypso and one for Odysseus.

Onto the content!

# Calypso

## Notes and Thoughts

- I finally have a stable part of my API! Unfortunately, this is currently just [`calypso_error`] and [`calypso_filety`] but it's progress! (Especially because `calypso_filety` was **SUPER** messy, so I ended up rewriting it completely. It's a lot better now.)
- I wrote a blog post called "The Calypso Manifesto" about why I made Calypso, what I want it to become, and what it's *not* going to become. If you'd like to read that, it's [available here](https://blog.thepuzzlemaker.info/posts/2021-02-22/calypso-manifesto.html).

## TODOs

Since pretty much everything is a TODO, I'm just going to write immediate TODOs here for now. :P

- Finish redesign of the language, including error handling.
- Rewrite/fix lexer according to new design
  - see https://github.com/calypso-lang/calypso/tree/wip-lexer-rewrite (WIP lexer rewrite branch, blocked on language redesign)
  - see {% github https://github.com/calypso-lang/calypso/issues/17 %}

[`calypso_error`]: https://crates.io/crates/calypso_error
[`calypso_filety`]: https://crates.io/crates/calypso_filety

# Odysseus

## Notes and Thoughts

- Oh goodness generics can be cursed. In order to reduce boilerplate, I created [these traits] for the Odysseus bytecode builder:
```rust
//! Traits used to make construction of bytecode builders much easier. These
//! may use complex or generally irritating generics, but it is much better
//! than repeating a lot of boilerplate. It is recommended that you read the
//! documentation before implementing any of these traits.

/// A trait used for "entries" of a bytecode [`Parent`], similar to a
/// [`std::collections::hash_map::Entry`].
pub trait Entry {
    /// The parent type of this entry.
    type Parent: Parent<<Self as Entry>::Element>;
    /// The element type of this entry.
    type Element: Element;

    /// Get the bytecode element behind this entry, if finished.
    ///
    /// # Implementors
    ///
    /// Do not re-implement this function. It has an implementation that is
    /// already sufficient for generalized functionality.
    fn get<'p>(&self, parent: &'p <Self as Entry>::Parent) -> Option<&'p <Self as Entry>::Element> {
        if !self.is_finished(parent) {
            panic!("cannot get an unfinished bytecode element");
        }
        parent.get(self.id())
    }

    /// Check if the bytecode element has been finished.
    ///
    /// # Implementors
    ///
    /// Do not re-implement this function. It has an implementation that is
    /// already sufficient for generalized functionality.
    fn is_finished(&self, parent: &<Self as Entry>::Parent) -> bool {
        parent.is_finished(self.id())
    }

    /// Enter the builder context and use the closure to build the bytecode
    /// element.
    ///
    /// # Implementors
    ///
    /// Do not re-implement this function. It has an implementation that is
    /// already sufficient for generalized functionality.
    fn enter(
        &mut self,
        parent: &mut <Self as Entry>::Parent,
        f: impl FnOnce(
            <<Self as Entry>::Element as Element>::Builder,
        ) -> <<Self as Entry>::Element as Element>::Builder,
    ) -> &mut Self {
        if self.is_finished(parent) {
            panic!("cannot enter a finished bytecode element");
        }
        let builder = f(parent.create_builder(self.id()));
        self.internal_build(parent, builder);
        self
    }

    /// Finish the bytecode element.
    ///
    /// # Implementors
    ///
    /// Do not re-implement this function. It has an implementation that is
    /// already sufficient for generalized functionality.
    fn finish<'p>(
        &mut self,
        parent: &'p mut <Self as Entry>::Parent,
    ) -> &'p <Self as Entry>::Element {
        if self.is_finished(parent) {
            panic!("cannot build a finished bytecode element")
        }
        parent.finish(self.id())
    }

    /// Get the ID of the bytecode element behind the entry.
    fn id(&self) -> <<Self as Entry>::Element as Element>::Id;

    /// An internal function to register changes caused by a builder. Do not
    /// call this function as it may cause errors.
    fn internal_build(
        &mut self,
        parent: &mut <Self as Entry>::Parent,
        builder: <<Self as Entry>::Element as Element>::Builder,
    );
}

/// A bytecode parent, i.e. a structure that contains one or more types of
/// bytecode elements.
pub trait Parent<Child>
where
    Child: Element,
{
    /// Check if a bytecode element is finished. This function should return
    /// `false` if the ID was not present within the parent. This function is
    /// called by [`Entry::is_finished`].
    fn is_finished(&self, id: Child::Id) -> bool;

    /// Get a reference to a bytecode element. This function is called by
    /// [`Entry::get`].
    fn get(&self, id: Child::Id) -> Option<&Child>;

    /// Create a builder from the specified bytecode element ID.
    ///
    /// # Panics
    ///
    /// This function may panic if the ID was not present within the parent,
    /// depending on the implementor.
    fn create_builder(&mut self, id: Child::Id) -> Child::Builder;

    /// Finish the bytecode element with the specified ID.
    ///
    /// # Panics
    ///
    /// This function may panic if the ID was not present within the parent,
    /// depending on the implementor.
    fn finish(&mut self, id: Child::Id) -> &Child;
}

/// A bytecode element, i.e. a single element in the bytecode, whether
/// high-level or low-level.
///
/// For example, all these are bytecode elements:
/// - Modules
/// - Functions
/// - Blocks
pub trait Element {
    /// The entry type of this bytecode element.
    type Entry: Entry;
    /// The builder type of this bytecode element, i.e. a structure that can
    /// construct a bytecode element. It should store changes within its own
    /// structure, then its corresponding [`Entry`] type should implement the
    /// [`Entry::internal_build`] function that writes these changes to the
    /// [`Parent`].
    type Builder;
    /// The ID type of this bytecode element. It is required to be [`Copy`] to
    /// ensure that IDs are small and also because of limitations with
    /// borrowing and traits.
    type Id: PartialEq + Eq + Copy;
}
```
I might just be lucky but these are the most cursed generics I've ever had to use. I'm not sure why I have to explicitly disambiguate (e.g. `<<Self as Entry>::Element as Element>::Builder`) but rustc complains at me if I don't. :'(

[these traits]: https://github.com/calypso-lang/calypso/blob/c9222c0304d55dd44d3427b610dd76d8d8c2947a/libs/odysseus/src/bc/traits.rs

## TODOs

- After the redesign of Calypso is done, solidify ABI and ISA for Odysseus.
- After that's done, I can complete the bytecode builder and then eventually the runtime. (And at that point, Odysseus is *basically* done! The runtime is going to take a while though.)

# Conclusion

Basically, it's going well! :)

If you find anything that I might've missed or you'd like more information on, feel free to comment or otherwise contact me!

Otherwise, have a good morning/afternoon/evening/night/etc.!
-James