Skip to main content
added 151 characters in body
Source Link
Eric Lippert
  • 14.9k
  • 4
  • 38
  • 57

This is a pretty reasonable start on a simple interpreter. Edward's suggestions are all good; a few additional suggestions:


interpret("+++++++++++++[->....

Please break up that long line. C allows you to break up literal strings

"like "
"this."

void goToLoopEnd(char** ip) {
...
void goToLoopStart(char** ip) {
...

If you wrote these instead as

char * findLoopEnd(char* ip) {
...
char * findLoopStart(char* ip) { 
...

then you have functions that are just as useful, but do not modify the input and are therefore testable with an independent unit test.

This also eliminates a double pointer; double pointers are hard to reason about, so if there is a cheap and easy way to get rid of them, go for it.

This also enables you to improve the contract of these methods; for example, you could return null if there is no matching bracket. Which then brings up...


Aside from that, my biggest problem with your interpreter is that it has undefined behaviours all over the place if the program is not valid. What happens if there is an unmatched [? What happens if the pointer goes before available memory? Bad stuff, that's what. A good interpreter is safe no matter what its input, and gives good diagnostics to the user when the input is bad. Always structure an interpreter so that it detects bad inputs, because it will get bad inputs.

In particular, note that Brainfuck does not specify what happens when the tape pointer becomes negative; does the tape "wrap around?", is it an error, is there supposed to be an infinite tape in both directions? The conservative thing to do is to make it an error, but you haven't even done that; you just do something undefined.


Once you have the undefined behaviours under control, consider the cost of doing a linear search for matching brackets all the time. It is almost always more efficient to scan the source code once, make a note of the locations of all matching pairs of brackets, and then when you hit a bracket you just do a lookup of the destination rather than a linear search for it. Yes, a linear search is fast if the program is small, but the program might not be small.

This step works to support the previous issue; if you record matching brackets before you run the program, you guarantee that every bracket is matched!

This is a pretty reasonable start on a simple interpreter. Edward's suggestions are all good; a few additional suggestions:


interpret("+++++++++++++[->....

Please break up that long line. C allows you to break up literal strings

"like "
"this."

void goToLoopEnd(char** ip) {
...
void goToLoopStart(char** ip) {
...

If you wrote these instead as

char * findLoopEnd(char* ip) {
...
char * findLoopStart(char* ip) { 
...

then you have functions that are just as useful, but do not modify the input and are therefore testable with an independent unit test.

This also enables you to improve the contract of these methods; for example, you could return null if there is no matching bracket. Which then brings up...


Aside from that, my biggest problem with your interpreter is that it has undefined behaviours all over the place if the program is not valid. What happens if there is an unmatched [? What happens if the pointer goes before available memory? Bad stuff, that's what. A good interpreter is safe no matter what its input, and gives good diagnostics to the user when the input is bad. Always structure an interpreter so that it detects bad inputs, because it will get bad inputs.

In particular, note that Brainfuck does not specify what happens when the tape pointer becomes negative; does the tape "wrap around?", is it an error, is there supposed to be an infinite tape in both directions? The conservative thing to do is to make it an error, but you haven't even done that; you just do something undefined.


Once you have the undefined behaviours under control, consider the cost of doing a linear search for matching brackets all the time. It is almost always more efficient to scan the source code once, make a note of the locations of all matching pairs of brackets, and then when you hit a bracket you just do a lookup of the destination rather than a linear search for it. Yes, a linear search is fast if the program is small, but the program might not be small.

This step works to support the previous issue; if you record matching brackets before you run the program, you guarantee that every bracket is matched!

This is a pretty reasonable start on a simple interpreter. Edward's suggestions are all good; a few additional suggestions:


interpret("+++++++++++++[->....

Please break up that long line. C allows you to break up literal strings

"like "
"this."

void goToLoopEnd(char** ip) {
...
void goToLoopStart(char** ip) {
...

If you wrote these instead as

char * findLoopEnd(char* ip) {
...
char * findLoopStart(char* ip) { 
...

then you have functions that are just as useful, but do not modify the input and are therefore testable with an independent unit test.

This also eliminates a double pointer; double pointers are hard to reason about, so if there is a cheap and easy way to get rid of them, go for it.

This also enables you to improve the contract of these methods; for example, you could return null if there is no matching bracket. Which then brings up...


Aside from that, my biggest problem with your interpreter is that it has undefined behaviours all over the place if the program is not valid. What happens if there is an unmatched [? What happens if the pointer goes before available memory? Bad stuff, that's what. A good interpreter is safe no matter what its input, and gives good diagnostics to the user when the input is bad. Always structure an interpreter so that it detects bad inputs, because it will get bad inputs.

In particular, note that Brainfuck does not specify what happens when the tape pointer becomes negative; does the tape "wrap around?", is it an error, is there supposed to be an infinite tape in both directions? The conservative thing to do is to make it an error, but you haven't even done that; you just do something undefined.


Once you have the undefined behaviours under control, consider the cost of doing a linear search for matching brackets all the time. It is almost always more efficient to scan the source code once, make a note of the locations of all matching pairs of brackets, and then when you hit a bracket you just do a lookup of the destination rather than a linear search for it. Yes, a linear search is fast if the program is small, but the program might not be small.

This step works to support the previous issue; if you record matching brackets before you run the program, you guarantee that every bracket is matched!

added 334 characters in body
Source Link
Eric Lippert
  • 14.9k
  • 4
  • 38
  • 57

This is a pretty reasonable start on a simple interpreter. Edward's suggestions are all good; a few additional suggestions:


interpret("+++++++++++++[->....

Please break up that long line. C allows you to break up literal strings

"like "
"this."

void goToLoopEnd(char** ip) {
...
void goToLoopStart(char** ip) {
...

If you wrote these instead as

char * findLoopEnd(char* ip) {
...
char * findLoopStart(char* ip) { 
...

then you have functions that are just as useful, but do not modify the input and are therefore testable with an independent unit test.

This also enables you to improve the contract of these methods; for example, you could return null if there is no matching bracket. Which then brings up...


Aside from that, my biggest problem with your interpreter is that it has undefined behaviours all over the place if the program is not valid. What happens if there is an unmatched [? What happens if the pointer goes before available memory? Bad stuff, that's what. A good interpreter is safe no matter what its input, and gives good diagnostics to the user when the input is bad. Always structure an interpreter so that it detects bad inputs, because it will get bad inputs.

In particular, note that Brainfuck does not specify what happens when the tape pointer becomes negative; does the tape "wrap around?", is it an error, is there supposed to be an infinite tape in both directions? The conservative thing to do is to make it an error, but you haven't even done that; you just do something undefined.


Once you have the undefined behaviours under control, consider the cost of doing a linear search for matching brackets all the time. It is almost always more efficient to scan the source code once, make a note of the locations of all matching pairs of brackets, and then when you hit a bracket you just do a lookup of the destination rather than a linear search for it. Yes, a linear search is fast if the program is small, but the program might not be small.

This step works to support the previous issue; if you record matching brackets before you run the program, you guarantee that every bracket is matched!

This is a pretty reasonable start on a simple interpreter. Edward's suggestions are all good; a few additional suggestions:


interpret("+++++++++++++[->....

Please break up that long line. C allows you to break up literal strings

"like "
"this."

void goToLoopEnd(char** ip) {
...
void goToLoopStart(char** ip) {
...

If you wrote these instead as

char * findLoopEnd(char* ip) {
...
char * findLoopStart(char* ip) { 
...

then you have functions that are just as useful, but do not modify the input and are therefore testable with an independent unit test.

This also enables you to improve the contract of these methods; for example, you could return null if there is no matching bracket. Which then brings up...


Aside from that, my biggest problem with your interpreter is that it has undefined behaviours all over the place if the program is not valid. What happens if there is an unmatched [? What happens if the pointer goes before available memory? Bad stuff, that's what. A good interpreter is safe no matter what its input, and gives good diagnostics to the user when the input is bad. Always structure an interpreter so that it detects bad inputs, because it will get bad inputs.


Once you have the undefined behaviours under control, consider the cost of doing a linear search for matching brackets all the time. It is almost always more efficient to scan the source code once, make a note of the locations of all matching pairs of brackets, and then when you hit a bracket you just do a lookup of the destination rather than a linear search for it. Yes, a linear search is fast if the program is small, but the program might not be small.

This step works to support the previous issue; if you record matching brackets before you run the program, you guarantee that every bracket is matched!

This is a pretty reasonable start on a simple interpreter. Edward's suggestions are all good; a few additional suggestions:


interpret("+++++++++++++[->....

Please break up that long line. C allows you to break up literal strings

"like "
"this."

void goToLoopEnd(char** ip) {
...
void goToLoopStart(char** ip) {
...

If you wrote these instead as

char * findLoopEnd(char* ip) {
...
char * findLoopStart(char* ip) { 
...

then you have functions that are just as useful, but do not modify the input and are therefore testable with an independent unit test.

This also enables you to improve the contract of these methods; for example, you could return null if there is no matching bracket. Which then brings up...


Aside from that, my biggest problem with your interpreter is that it has undefined behaviours all over the place if the program is not valid. What happens if there is an unmatched [? What happens if the pointer goes before available memory? Bad stuff, that's what. A good interpreter is safe no matter what its input, and gives good diagnostics to the user when the input is bad. Always structure an interpreter so that it detects bad inputs, because it will get bad inputs.

In particular, note that Brainfuck does not specify what happens when the tape pointer becomes negative; does the tape "wrap around?", is it an error, is there supposed to be an infinite tape in both directions? The conservative thing to do is to make it an error, but you haven't even done that; you just do something undefined.


Once you have the undefined behaviours under control, consider the cost of doing a linear search for matching brackets all the time. It is almost always more efficient to scan the source code once, make a note of the locations of all matching pairs of brackets, and then when you hit a bracket you just do a lookup of the destination rather than a linear search for it. Yes, a linear search is fast if the program is small, but the program might not be small.

This step works to support the previous issue; if you record matching brackets before you run the program, you guarantee that every bracket is matched!

added 155 characters in body
Source Link
Eric Lippert
  • 14.9k
  • 4
  • 38
  • 57

This is a pretty reasonable start on a simple interpreter. Edward's suggestions are all good; a few additional suggestions:


interpret("+++++++++++++[->....

Please break up that long line. C allows you to break up literal strings

"like "
"this."

void goToLoopEnd(char** ip) {
...
void goToLoopStart(char** ip) {
...

If you wrote these instead as

char * findLoopEnd(char* ip) {
...
char * findLoopStart(char* ip) { 
...

then you have functions that are just as useful, but do not modify the input and are therefore testable with an independent unit test.

This also enables you to improve the contract of these methods; for example, you could return null if there is no matching bracket. Which then brings up...


Aside from that, my biggest problem with your interpreter is that it has undefined behaviours all over the place if the program is not valid. What happens if there is an unmatched [? What happens if the pointer goes before available memory? Bad stuff, that's what. A good interpreter is safe no matter what its input, and gives good diagnostics to the user when the input is bad. Always structure an interpreter so that it detects bad inputs, because it will get bad inputs.


Once you have the undefined behaviours under control, consider the cost of doing a linear search for matching brackets all the time. It is almost always more efficient to scan the source code once, make a note of the locations of all matching pairs of brackets, and then when you hit a bracket you just do a lookup of the destination rather than a linear search for it. Yes, a linear search is fast if the program is small, but the program might not be small.

This step works to support the previous issue; if you record matching brackets before you run the program, you guarantee that every bracket is matched!

This is a pretty reasonable start on a simple interpreter. Edward's suggestions are all good; a few additional suggestions:


interpret("+++++++++++++[->....

Please break up that long line. C allows you to break up literal strings

"like "
"this."

void goToLoopEnd(char** ip) {
...
void goToLoopStart(char** ip) {
...

If you wrote these instead as

char * findLoopEnd(char* ip) {
...
char * findLoopStart(char* ip) { 
...

then you have functions that are just as useful, but do not modify the input and are therefore testable with an independent unit test.

This also enables you to improve the contract of these methods; for example, you could return null if there is no matching bracket. Which then brings up...


Aside from that, my biggest problem with your interpreter is that it has undefined behaviours all over the place if the program is not valid. What happens if there is an unmatched [? What happens if the pointer goes before available memory? Bad stuff, that's what. A good interpreter is safe no matter what its input, and gives good diagnostics to the user when the input is bad. Always structure an interpreter so that it detects bad inputs, because it will get bad inputs.


Once you have the undefined behaviours under control, consider the cost of doing a linear search for matching brackets all the time. It is almost always more efficient to scan the source code once, make a note of the locations of all matching pairs of brackets, and then when you hit a bracket you just do a lookup of the destination rather than a linear search for it. Yes, a linear search is fast if the program is small, but the program might not be small.

This is a pretty reasonable start on a simple interpreter. Edward's suggestions are all good; a few additional suggestions:


interpret("+++++++++++++[->....

Please break up that long line. C allows you to break up literal strings

"like "
"this."

void goToLoopEnd(char** ip) {
...
void goToLoopStart(char** ip) {
...

If you wrote these instead as

char * findLoopEnd(char* ip) {
...
char * findLoopStart(char* ip) { 
...

then you have functions that are just as useful, but do not modify the input and are therefore testable with an independent unit test.

This also enables you to improve the contract of these methods; for example, you could return null if there is no matching bracket. Which then brings up...


Aside from that, my biggest problem with your interpreter is that it has undefined behaviours all over the place if the program is not valid. What happens if there is an unmatched [? What happens if the pointer goes before available memory? Bad stuff, that's what. A good interpreter is safe no matter what its input, and gives good diagnostics to the user when the input is bad. Always structure an interpreter so that it detects bad inputs, because it will get bad inputs.


Once you have the undefined behaviours under control, consider the cost of doing a linear search for matching brackets all the time. It is almost always more efficient to scan the source code once, make a note of the locations of all matching pairs of brackets, and then when you hit a bracket you just do a lookup of the destination rather than a linear search for it. Yes, a linear search is fast if the program is small, but the program might not be small.

This step works to support the previous issue; if you record matching brackets before you run the program, you guarantee that every bracket is matched!

Source Link
Eric Lippert
  • 14.9k
  • 4
  • 38
  • 57
Loading