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

Use Rust compiler to avoid common mistakes

sunjay opened this issue · comments

We should see if there is some way to get the compiler and type system to prevent us from forgetting things like closing a loop, freeing allocated memory, move back to the left after moving right, etc.

It's too easy to forget to do that right now and it's causing tons of bugs. See codegen/if_condition.rs for a file full of these problems.

The giant documentation comment at the top of codegen isn't enough.

Possible Options

Closures

instructions.move_to(pos, || {
     //...generate more instructions...
});

instructions.jumps(cond_pos, || {
     //...generate more instructions...
});

Pros:

  • nice syntax
  • automatically ensures balance declaratively, no type system magic required

Cons:

  • might run into lifetime issues if we have multiple of these in a row (needs to be tested)
  • not totally flexible since we might actually want one of those instructions without the other (we could probably just leave in the current functions for that use case)
    What's the equivalent of this?
    instructions.move_right_by(cond_cell);
    instructions.jump_forward_if_zero();
    instructions.move_left_by(cond_cell);

Structs

This is probably the best option. It's reasonably flexible and doesn't suffer the downsides of the closure method.

// must_use prevents this from being ignored when returned by a function
#[must_use]
struct Movement {
    right: usize,
}

impl Movement {
    // This consumes self, so the borrow checker will complain if this is used more than once
    fn left(self) {
        //...
    }
}

fn move_right_by(n: usize) -> Movement {
    //...
    Movement {right: n}
}

Pros:

  • Completely flexible, easy enough to add to existing code

Cons:

  • Lots of initial boilerplate to setup (individual structs for each operation) - not that bad