sunjay / brain

A high level programming language that compiles into the brainfuck esoteric programming language

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Replace stdio interface with something more sustainable

sunjay opened this issue · comments

The current stdio interface looks something like this:

let mut b: [u8; 5];
stdin.read(b);
stdout.write(b"b = ", b, b"\n");

From a design point of view, this is a pretty simple, magical way of defining IO that relies heavily on the compiler to implement it properly. This design was always meant to be temporary and eventually replaced with something more robust and strongly typed.

After some reflection, I have come to the conclusion that for the sake of backwards compatibility, it would be better to modify these functions to work a bit differently. There will still be some magic for the time being, but for the most part this should be fairly straightforward. The idea with this new design is that it should enable people to write code that will be backwards compatible for the most part. That is, we want to guarantee that at some level it will not require large changes to upgrade this to newer versions of the compiler. I'm being careful with the wording here because there will be some flexibility with breakages, but nothing so severe that it largely impacts people using the compiler. Had the design stayed as it is right now, these problems would be much worse.

Though backwards compatibility and breakages are an important concern, I am okay with some (read: very little) breakage if the compiler can generate a useful error that provides a clear (and hopefully easy) path to upgrade.

New Design: Standard Input

The new design is based on Rust's Read trait. We're going to be using the Read::read_exact() interface.

Code like this:

let mut b: [u8; 5];
stdin.read(b);

Will become:

let mut b: [u8; 5];
stdin.read_exact(b);

In the future when references are implemented, this will further become:

let mut b: [u8; 5];
stdin.read_exact(&mut b);

Though that is a small breakage I am willing to accept.

While this API may look very similar to the old one, it has a couple of important differences:

  • The old read() accepted multiple/variadic arguments. This new version only accepts a single argument
  • The old read() accepted any type. This new version only accepts [u8; N]
  • The old read() would return false if the read on any of its arguments failed (giving you no information about what actually failed). This new version will panic if the read fails
    • The read can fail if there are not enough bytes to read (EOF is reached before reading enough) or if something else more catastrophic happens. See Read::read_exact() for more info..
    • If you're not familiar with what a panic is, it's a Rust language concept that basically means the program quits with an error. We emulate that in Brainfuck by printing a message and entering an infinite loop.
    • Once algebraic datatypes are implemented, this panic will be replaced by a Result type which will generate a compiler error if it is ignored, thus making explicit error handling possible
    • To panic, use the brainfuck feature described here

New Design: Standard Output

For stdout, the change is much simpler. We're just replacing stdout.write with stdout.print since that is closer to what the function does and easier to replace in the future.

TODO

  • Update examples to use the new API
  • Update changelog if necessary
  • Update internal implementation of stdin.read
  • Update internal implementation of stdout.write to stdout.print
  • Update internal implementation of stdout.writeln to stdout.println