Okay, to answer my own question, there is a way around.
Compile time vs. runtime
Calculating the number of <
's statically is indeed impossible, as few people have suggested. Sometimes whether code is executed depends on some input and there's no way around that. An example: ,[>]
So the only way to do it is to search for cells at runtime. If you're able to find the beginning of the memory space (address 0), you can just jump 2 times to the right to get to address 2.
Finding a zero is easy in Brainfuck: [<] # do something here
However, you don't want to stop at some arbitrary cell that happens to have the value 0
, you need to make sure it's the actual beginning of the memory zero!
This is really inconvenient. Whichever value you choose to represent the starting address with, it can accidentally show up in the memory.
Dividing the memory space
So I divided the memory in what I call memory space and value space. Odd cells are values, even cells are "addresses". Well, there're not exactly unique numbers identifying cells, but more of a "ladder" that you use to move around.
Here's the layout of the memory, after it's mapped:
memory = [0, 0, 1, 0, 1, 0 ...]
start ^ ^ addr ^ value
Now every time you search for a location, you go left <
once to enter the address space, then loop until you find a zero, then move right n
times.
This schema requires you to fill the memory with alternating 1
's and 0
's before the program is executed. Given a fixed memory size, the compiler can generate code that does this for you.
This solution is not perfect. To begin with, you lose half of the memory. Filling it up with 1
's is also annoying and costs you time at the start of each program.
Appendix: marking the start with a "1"
You might be thinking: why not invert the schema and mark address zero with a 1
and leave all other addresses to be 0
? That way you don't need to fill up the memory with 1
's.
Let's try.
The code we need, translated into words would be:
- go left one cell to enter the address space
- if the value is 0, then move left 2 cells (keep searching)
- else (you've reached the start) go right N places
You need to write an if / then / else
construct. In Brainfuck this means copying some values to temporary locations (we've got plenty of those!) then executing the correct branch, then cleaning up.
But in our case "executing the code" means "go to some other location and never come back" so it's impossible to clean up the temporary variables. And if they're not 0
you won't find the start again.
I tried to find a way around this but I couldn't.
Here's the code for the compiler: https://gist.github.com/whoeverest/3b15f2a06fc32333f342