/* 64kb -1b each
*
* The Brainfuck spec says that memory should be 30000 bytes, but I prefer to
* use a whole 64kb for both code and memory.
*
*/
#define BRAINF_CODESIZE 65535
#define BRAINF_MEMSIZE 65535
FILE* fp;
unsigned char* code, *memory;
short unsigned int codeptr, memptr;
int main(int argc, char* argv[])
{
if (argc != 2)
exit(1);
while (!feof(fp))
{
fread(code, BRAINF_CODESIZE -1, 1, fp);
}
/*** Interpreting begins ***/
while (code[codeptr])
{
switch (code[codeptr])
{
case '>':
++memptr;
++codeptr;
break;
case '<':
--memptr;
++codeptr;
break;
case '+':
++memory[memptr];
++codeptr;
break;
case '-':
--memory[memptr];
++codeptr;
break;
case '.':
putchar(memory[memptr]);
++codeptr;
break;
case ',':
memory[memptr] = getchar();
++codeptr;
break;
/* :NOTE: Some kind of error handling should take
* place if no ']' is found.
*/
case '[':
if (!memory[memptr])
{
while ((code[codeptr] != ']') && (code[codeptr]))
{
++codeptr;
}
}
else
++codeptr;
break;
/* :NOTE: Some kind of error handling should take
* place if no '[' is found.
*/
case ']':
if (memory[memptr])
{
while ((code[codeptr] != '[') && (codeptr))
--codeptr;
}
else
++codeptr;
break;
/* invalid instruction
* For now we will just continue executing when this happens.
* It should ignore characters 10, 13, & 32 anyways.
*/
default:
++codeptr;
break;
}
}
Please give me feedback if I did anything wrong (eg. that is poor style), or what you would have done that would have been more efficient or readable or whatever.
Name:
Anonymous2009-08-31 2:14
>>1
Your implementation of the loop is kind of pathetic. Instead, you should push the location of the last '[' onto a stack, so you can jump directly to it.
Tomorrow I'll see if I can find the one I wrote for an embedded MIPS emulator a few years ago, and show you how it's done.
fucking fail. Loops are done so horridly I want to shoot myself. Not only are they ugly as fuck but you can't nest them like in BF. [ is while(memory[memptr]){ ] is }
if you had something like this: [[[ ]]]
if the first bracket failed you wouldn't just to the first ] you see, you would go to the matching one.
learn2jumpstack
Name:
The Amazing Anus!Anus3nMVO22009-08-31 3:04
>>9
Ah yes, thanks for pointing that out. I see I'll need to redesign this thing more than I thought.
Name:
Anonymous2009-08-31 3:48
; Common lisp
(defconstant +bf-memory+ 30000)
(defparameter *memory* (make-array +bf-memory+
:initial-element 0
:element-type '(unsigned byte 8)))
(defun bf (str &optional (index 0)
(position 0))
(loop for c across (subseq str index)
for index from index
do
(case c
(#\+ (incf (aref *memory* position)))
(#\- (decf (aref *memory* position)))
(#\> (when (= (incf position) +bf-memory+)
(setq position 0)))
(#\< (if (zerop position)
(setq position (1- +bf-memory+))
(decf position)))
(#\. (princ (code-char (aref *memory* position))))
(#\, (setf (aref *memory* position) (char-code (read-char))))
(#\[ (unless (zerop (aref *memory* position))
(loop until (zerop (bf str (1+ index) position)))))
(#\] (return (aref *memory* position))))))
Written just now. Use like (bf "++<>+>-"). Memory is circular.
Name:
Anonymous2009-08-31 6:32
If this was inspired by Xarn's latest blog post, why didn't you just look at the via-C compiler he posted for direction?
- Codesize and memsize should probably be 65536 instead of 65535. Remember that a size of N means that you can use elements 0 .. (N-1).
- You're relying on integer wraparound at 65536, which may or may not be correct depending on architecture. (Actually, you're relying on integer wraparound at 65535; see above.) You can use uint16_t instead if you need an unsigned integer that is always 16 bits.
- You're not checking the result of the two malloc calls. Shame on you.
- The code reading is wrong. It may read, say, 1024 bytes in the first iteration, stop on a (temporary) read error, notice that it's not yet at EOF, start again, and overwrite these 1024 bytes in the next fread call. Either stop in case of a read error and remove the loop, or handle it properly and keep tabs on the number of bytes successfully read. Also, the -1 here is wrong (the second argument to fread is a size, not a maximum value, so there's no need to subtract 1 from the real size).
- The '[' and ']' handlers are wrong, as has already been pointed out. >>5 is a good solution, but it requires you to keep a more complex state than you're currently using; if you want your implementation to be as simple and stateless as possible, you could use something like the following:
case '[':
if (!memory[memptr])
{
int bracketdepth = 1;
while (backetdepth != 0)
{
++codeptr;
if (code[codeptr] == ']') bracketdepth--;
if (code[codeptr] == '[') bracketdepth++;
}
++codeptr;
}
else
++codeptr;
break;
It IS still a bit ugly though (although that may be more or less the point of anything brainfuck-related ;) )
- A minor stylistic issue: your code becomes somewhat clearer if you increment codeptr at the very end of the main loop, instead of at each branch. This has the downside of making [ and ] somewhat more complicated, so you have to decide for yourself whether it's actually an improvement.
- I'm pretty sure all characters other than +-<>.,[] are well-defined as being comments, so your default: clause is perfectly fine.
- Finally, you don't return a value (in particular, the value 0) at the end of main, something the compiler will probably have warned you about.
Name:
Anonymous2009-08-31 16:27
Remember that nice Brainfuck implementations really only need to guarantee 9999 cells, so unless you're writing it specifically to accommodate some non-conforming Brainfuck code, you don't even have to fuck around with mallocating.
Name:
The Amazing Anus!Anus3nMVO22009-08-31 16:37
>>15
ooh very good! I'll fix all of these things today.
Name:
Anonymous2009-08-31 16:58
You can use uint16_t instead if you need an unsigned integer that is always 16 bits.
This doesn't work for me.
brainfuck.c [Warning] data definition has no type or storage class
Apparently uint16_t isn't defined anywhere for my shitty setup.
>>8
You're right. I should have named "code" and "memory" with the letters "ptr" at the end, instead of naming the array index like that, shouldn't I? Please explain anything else I did wrong with pointer semantics.
Just knocked off a few of those things. I'll fix the whole nested brackets thing later.
Name:
Anonymous2009-08-31 19:04
>>20
>I should have named "code" and "memory" with the letters "ptr" at the end, instead of naming the array index like that, shouldn't I?
I (>>15) would suggest keeping "code" and "memory" as they are, and replacing "codeptr" and "memoryptr" by "codeindex" and "memoryindex", respectively. The ptr variables are wrong, because they aren't pointers, whereas "code" and "memory" describe their functions perfectly.
>>20
I mean the way you've declared your pointers.
unsigned char* bob, jill;
This is counter-intuitive, because it creates one unsigned char and one pointer to an unsigned char. jill is a regular unsigned char. What I mean is you should be defining your pointers as such:
unsigned char *bob, *jill;
Name:
Anonymous2009-09-01 7:36
>>22
Of course, the simple solution to this problem is to never declare multiple variables in a single declaration, which is often considered bad style anyway.
>>23
That still doesn't cover the usage semantics. You dereference by using unsigned char bobval = *bob; and then you have a straight-up variable that's copied from the data that bob points to.
Pointers are not proper types, they are variable modifiers. They are used on a per-variable basis, and as such, should always be hugging the variable they modify semantically.
>>25
What load of bollocks. Pointers are not proper types,
In C, there are complete and incomplete types. Pointers belong to the former. [pointers] should always be hugging the variable they modify semantically.
Pointers might 'hug' (what fucked up term is that? where did you find the relation between point and hug?), the address of something that is not an object. Consider the pointer to the incomplete type int[]: int (*foo)[];
>>26
Not bollocks. Pointers are not proper types. You cannot define a purely pointer variable. There must be some type to point to, if nothing else, void *. They might be complete or incomplete, it doesn't really matter. That's irrelevant to what I was saying.
There is no relationship between point and hug. There is a spacial relationship between the operator and the operand which constitutes hugging.
/* 64kb each
*
* The Brainfuck spec says that memory should be 30000 bytes, but I prefer to
* use a whole 64kb for both code and memory.
*
*/
#define BRAINF_CODESIZE 65536
#define BRAINF_MEMSIZE 65536
/*** Interpreting begins. Ends on NULL in codebyte. ***/
while (code[codeindex])
{
switch (code[codeindex])
{
case '>':
++memindex;
++codeindex;
break;
case '<':
--memindex;
++codeindex;
break;
case '+':
++memory[memindex];
++codeindex;
break;
case '-':
--memory[memindex];
++codeindex;
break;
case '.':
putchar(memory[memindex]);
++codeindex;
break;
case ',':
memory[memindex] = getchar();
++codeindex;
break;
case '[':
if (!memory[memindex])
{
++bracketdepth;
while (bracketdepth)
{
if (!code[++codeindex]) /* Exit if NULL */
exit(1);
if (code[codeindex] == '[')
++bracketdepth;
if (code[codeindex] == ']')
--bracketdepth;
}
}
else
++codeindex;
break;
case ']':
if (memory[memindex])
{
--bracketdepth;
while (bracketdepth)
{
if (codeindex-- == 0) /* Exit if need to go */
exit(1); /* past first codebyte */
if (code[codeindex] == '[')
++bracketdepth;
if (code[codeindex] == ']')
--bracketdepth;
}
}
else
++codeindex;
break;
/* non-instruction character */
default:
++codeindex;
break;
}
}
free(code);
return 0;
}
Again, tell me if I did anything wrong (eg. that is poor style), or what I could have done better, like more efficiently or would be more readable.
And here's a little program I found that will print out the Fibonacci numbers:
>>28 unsigned char* is a problem semantically. I used an example to illustrate why this is a problem. Since you had difficulty understanding this, I must conclude that IHBT.
>>29
Global variables are considered poor form in many cases, just a note. It's fine for this particular program for a number of reasons, but that may not always be the case.
You could also abstract the logic into separate function(s) so as to implement some OOP PRINCIPLES, though that's largely superfluous and stupid in this case.
Having different exit codes to indicate the exit point is generally a good idea.
Another idea if you're interested would be checking the validity of the Brainfuck program first and issue warnings of some sort if it lacks validity. That's a bit harder than simply executing the program, but as far as lexical processing, it's hard to get any easier than that.
HOWEVER, when purely performing +-<> operations, my bf and the qdbf can crank out about 60,000 instructions in ~0.03 seconds. Yours (bfanon) takes 0.15 seconds to do the exact same thing.
That makes >>34-chan's interpreter 5 times slower at executing instructions than the other 2. Which is funny because mine takes 0.709 seconds to do the little benchmark thingy I wrote, whereas anon's does it fastest with qdbf close behind. I guess this just means that my bracket code really sucks.
Name:
Anonymous2009-09-03 6:44
>>29
All your global variables are in fact local to the main function. This is pretty pedantic in this specific case, since main is the only function, but it's a bad habit to develop - avoid global variables unless you really, really can't.
The bracketdepth variable doesn't even need to be declared in the whole main function - you only need it in the [ and ] branches, without saving its state between two executions of [ or ]. In general, try to have a variable declared in the smallest possible scope; that way, I don't have to read the code in detail to find out whether the variable is used outside the relevant blocks. This greatly enhances readability and maintainability.
Oh, and you forgot to free(memory) at the end.
>>36 So as you can see, my interpreter sucks ass.
It's slowest, but that doesn't mean it sucks ass. For instance, it's also the simplest and easiest to read interpreter. Running speed is not the main goal of an interpreter, after all. If you want raw speed, write a compiler instead.
Fine. Go improve mine by improving load time optimization: put in an array of numbers to tell you how many times each symbol appears in a row. Like +++>>----< would in memory be:
>>41
If you're going for load time optimization, you can speed up the loops even more if, instead of using a stack, determine during load time which address to jump to for each [ and ] instruction.
Name:
The Amazing Anus!Anus3nMVO22009-09-03 14:44
All times are averages after several runs.
$ time bf bench.bf
real 0.649 secs
$ time qdbf bench.bf
real 0.220 secs
$ time bfanon34 bench.bf
real 0.186 secs
Ok, it seems that I can't do anything to get >>38-chan's interpreter to accept my bench.bf program. It also hangs forever if I try to run the quine on it... same with the program that prints the Fibonacci numbers.
The only thing that actually works is "Hello, World!" I get corrupted stack and segmentation fault when I try to run the Fibonacci program on it. I'll see if I can find out what its problem is.
Here are some typographical errors, spotted by various readers. Page numbers refer to the online versions (which differ from the printed version).
• Page 128, second to last paragraph of section 3.7, first sentence. "addis" should be "is a".
• Page 134, the code for i'. The "n" under the big brace symbol should say "n pairs".
• Page 135. Equation 3.36 should have "NConstr", not "Constr".
• Page 230, second paragraph. "There one final gloss..." should be "There is one final gloss...".
• Page 278, the definition of nfib. Replace "n==0" by "n<=1".
>>43
I didn't test it, just got it to compile (actually it compiled straight away) and checked the Hello, World program on Wikipedia. That being said it runs your fibs program fine, how are you compiling/running? If you post the line number of the segfault I'll have a look.
Name:
Anonymous2009-09-04 1:47
>>51
I compiled it with gcc -o bfanon38 bfanon38.c
Ok, now I am able to successfully run the Fibonacci thing... But bench.bf still causes yours to hang. I could use a better benchmarking program, but it works fine on the other 3 so I'm not sure what it is about yours.
>>57
yes there is... or was.
i remember several times in which i have been banned because evidently i am a spambot. each time it happened i was posting four or five urls in the same post, so i just put two and two together.
Name:
Anonymous2009-09-04 6:33
There is an autobanner and it's apparently broken. I was deemed a spambot when I was making a regular posts. No URLs, no repetition.
>>60
Almost; I got an offer from Firefox to save a file with such name.
Name:
Anonymous2009-09-04 17:56
I think that bfanon38 hanging is something to do with not supporting cell wrap-around. Working on a better bench.bf at the moment 'cause the last one is shit.
>>43
The time given under "user" is a far better description of how fast a program runs than "real". Anyway, If you want fast, try beef or the program that beef stole its code from. It matches brackets while the code is loading by loading it into a binary tree. Run-time bracket matching is slower.
Name:
Anonymous2009-11-14 9:41
Why do people write brainfuck interpreters? The thing is so simple that you can write brainfuck in brainfuck. If you're using C, compile the damn code already
>>68
There has been more than one brainfuck compiler posted, It's probably personal preference
Name:
Anonymous2009-11-14 10:30
>>68
C++ is so simple that you can write C++ in C++.
Name:
Anonymous2009-11-14 11:03
//`'''```,
o // LISP `.,
,....OOo. .c;.',,,.'``.,,.`
.' ____.,'.//
/ _____ \___/.'
| / || \\---\|
|| || \\ ||
co co co co
Name:
Anonymous2009-11-15 19:06
#include <iostream>
using std::cin;
using std::cout;
using std::cerr;
using std::endl;
#define MS (65536)
struct state {
unsigned char * tape;
size_t head;
static state s;
private:
state() : tape(new unsigned char[MS]), head() {}
};
state state::s;
state & s = state::s;
template <char c, int times = 1>
struct insn {
enum { C = c };
enum { T = 0 };
void operator () () const {}
};
template <typename B, typename I>
struct block : B {
typedef B base;
typedef I insn_;
>>73 C:\Dev-Cpp\projects\bfpp.cpp:35: error: a function call cannot appear in a constant-expression
C:\Dev-Cpp\projects\bfpp.cpp:36: error: missing `>' to terminate the template argument list
C:\Dev-Cpp\projects\bfpp.cpp:36: error: missing `>' to terminate the template argument list
C:\Dev-Cpp\projects\bfpp.cpp:36: error: template argument 2 is invalid
C:\Dev-Cpp\projects\bfpp.cpp:36: error: expected unqualified-id before '{' token
C:\Dev-Cpp\projects\bfpp.cpp:39: error: `>>' should be `> >' within a nested template argument list
C:\Dev-Cpp\projects\bfpp.cpp: In member function `block<block<B, I>, insn<c, 1> > block<B, I>::operator,(const insn<c, 1>&)':
C:\Dev-Cpp\projects\bfpp.cpp:40: error: `>>' should be `> >' within a nested template argument list
C:\Dev-Cpp\projects\bfpp.cpp: At global scope:
C:\Dev-Cpp\projects\bfpp.cpp:50: error: `>>' should be `> >' within a nested template argument list
C:\Dev-Cpp\projects\bfpp.cpp: In member function `block<block<void, void>, insn<c, 1> > block<void, void>::operator,(const insn<c, 1>&)':
C:\Dev-Cpp\projects\bfpp.cpp:50: error: `>>' should be `> >' within a nested template argument list
C:\Dev-Cpp\projects\bfpp.cpp: At global scope:
C:\Dev-Cpp\projects\bfpp.cpp:59: error: missing `>' to terminate the template argument list
C:\Dev-Cpp\projects\bfpp.cpp:59: error: template argument 1 is invalid
C:\Dev-Cpp\projects\bfpp.cpp:59: error: missing `>' to terminate the template argument list
C:\Dev-Cpp\projects\bfpp.cpp:59: error: template argument 2 is invalid
C:\Dev-Cpp\projects\bfpp.cpp:75: error: missing `>' to terminate the template argument list
C:\Dev-Cpp\projects\bfpp.cpp:75: error: template argument 1 is invalid
C:\Dev-Cpp\projects\bfpp.cpp:75: error: missing `>' to terminate the template argument list
C:\Dev-Cpp\projects\bfpp.cpp:75: error: template argument 2 is invalid
C:\Dev-Cpp\projects\bfpp.cpp:161: error: ISO C++ forbids declaration of `static_assert' with no type
C:\Dev-Cpp\projects\bfpp.cpp:161: error: expected `;' before '(' token
C:\Dev-Cpp\projects\bfpp.cpp:170: error: ISO C++ forbids declaration of `static_assert' with no type
C:\Dev-Cpp\projects\bfpp.cpp:170: error: expected `;' before '(' token
C:\Dev-Cpp\projects\bfpp.cpp: In function `int main()':
C:\Dev-Cpp\projects\bfpp.cpp:187: error: ISO C++ forbids declaration of `prog' with no type
C:\Dev-Cpp\projects\bfpp.cpp:188: error: expected `)' before "BFCODE"
C:\Dev-Cpp\projects\bfpp.cpp:189: error: cannot convert `block<void, void>' to `int' in initialization
C:\Dev-Cpp\projects\bfpp.cpp:190: error: `prog' cannot be used as a function
Dev-Cpp Compiling Sepples0x with a Sepples2003 compiler
( ≖‿≖)
Name:
The Amazing Anus2009-11-15 22:42
Ok, I am using the bench.bf provided by >>73 (thx btw)
I compile each one with gcc -s -Os.
This is how they match up with the "real" provided by time:
bf 13.291 seconds (My own, as in >>29 )
qdbf 4.774 seconds (QDBF, forget where I found it)
bfanon34 6.287 seconds (Provided by >>34 )
bfanon38 12.527 seconds (Provided by >>38 )
I can't bench >>73 because I still can't get it to compile.
Name:
Anonymous2009-11-15 22:43
>>81
Yeah, I know. What do you recommend? Is gcc 4.4.2 (or whatever the newest stable build is) capable of building it?
Name:
Anonymous2009-11-16 17:32
>>77 Why thank you, Dear! I'm very fond of you too.
Here's a version that should compile w/o any trickery.
#include <iostream>
using std::cin;
using std::cout;
using std::cerr;
using std::endl;
#define MS (65536)
struct state {
unsigned char * tape;
size_t head;
static state s;
private:
state() : tape(new unsigned char[MS]), head() {}
};
state state::s;
state & s = state::s;
template <char c, int times = 1>
struct insn {
enum { C = c };
enum { T = 0 };
void operator () () const {}
};
template <typename B, typename I>
struct block : B {
typedef B base;
typedef I insn_;
Compile w/ this to get the benchmark previously mentioned: g++ -O2 -DBFCODE=,r,p,p,b,l,p,p,p,p,p,p,p,p,p,p,p,p,p,r,m,e,l,b,b,r,p,r,p,l,l,m,e,r,b,l,p,r,m,e,p,p,p,p,p,p,p,p,b,r,p,p,p,p,p,p,p,p,l,m,e,r,d,b,m,e,l,l,r,p,p,p,p,p,p,p,p,p,p,b,r,p,p,p,p,p,p,p,p,p,p,b,r,p,p,p,p,p,p,p,p,p,p,b,r,p,p,p,p,p,p,p,p,p,p,b,r,p,p,p,p,p,p,p,p,p,p,b,r,p,p,p,p,p,p,p,p,p,p,b,r,p,p,p,p,p,p,p,p,p,p,b,m,e,l,m,e,l,m,e,l,m,e,l,m,e,l,m,e,l,m,e,l,m,e,p,p,p,p,p,p,p,p,p,p,d
I reckon there's a bug hiding somewhere in there, as it emits a different/wrong result when compiled with cl.exe, than it does when compiled with g++. (I used the 20091112 snapshot of g++-4.5.)
Name:
Anonymous2009-11-16 17:36
what is that -DBFCODE bullshit for?
Name:
The Amazing Anus2009-11-16 17:44
>>84
I don't know why this one doesn't even take a file as input but it runs in 1.560 seconds.
Now let's see who can write the fastest Brainfuck interpreter. I want a harder benchmark program, and I want >>84 to take a file as input before I accept it into the contest.
A CL "compiler" for bf could be done using macros or just COMPILE, add some declarations and it might even produce some decent code on some implementations.
... but I doubt it would exceed the Perl to C compiler's performance, if at all. A better solution might be to go platform specific and generate machine code (or assembly) directly, which might just be faster, especially if one makes it an optimizing compiler...
>>92 ~ % perl bfcc.pl bench.bf|gcc -O3 -ffast-math -xc -std=gnu99 -xc -o bf1 -
~ % time ./bf1
ZYXWVUTSRQPONMLKJIHGFEDCBA
./bf1 0.04s user 0.00s system 98% cpu 0.039 total
~ % runghc bf2c.hs <bench.bf |gcc -O3 -ffast-math -xc -std=gnu99 -xc -o bf2 -
~ % time ./bf2
ZYXWVUTSRQPONMLKJIHGFEDCBA
./bf2 0.01s user 0.00s system 97% cpu 0.013 total
Name:
Anonymous2009-11-17 19:42
I HATE women. I never had a girlfriend and never will. The only times I got laid was when I paid a woman or promised her something. I'm never going to hold hands with a chick, kiss a girl intimately because we're in love, or any of the other shit that human beings were made to do. I guess that I'm suppose to be happy masturbating every fucking night. I'm a man with sexual urges and can't get with a female. I'm suppose to be alright with that? THERE IS A FUCKING CURSE ON MY LIFE. A CURSE THAT PREVENTS ANY FEMALE FROM LIKING ME. Oh I forgot, I do get interest from fat chicks and I'm not attracted to fat chicks.
I don't give a fuck anymore. I'm going to become the biggest asshole in the world. I tried the whole being considerate thing and it got me nowhere. If people can't handle my newfound harshness, then bring it on. BECAUSE I DON'T GIVE A FUCK. I DON'T GIVE A FUCK. I DON'T GIVE A FUCK.
I get happy when I hear about some college slut getting murdered or injured in a hit and run. "oh she was a beautiful and talented girl, how could this happen." I don't know but I'm glad it did.