Brainfuck principle and C language implementation

Brainfuck is a special programming language that has only 8 instructions and is very simple. Brainfuck's source code consists of a series of instructions that can manipulate an array and output or input data.

To code Brainfuck in C, follow these steps:

  1. Define an array of char type as Brainfuck's data space.

  2. Read the Brainfuck source code entered by the user.

  3. According to the read source code, use the switch-case statement to realize the 8 instructions of Brainfuck, including: +, -, >, <, .,,, [,].
    > : Move the pointer to the right. Returns an error if the pointer has reached the end of the array.
    < : Move the pointer to the left. Returns an error if the pointer has reached the beginning of the array.
    + : Add 1 to the value of the memory block pointed to by the pointer.
    - : Decrement 1 from the value of the memory block pointed to by the pointer.
    . : Output the value of the memory block pointed by the pointer. (ASCII code)
    , : wait for input, and store the input characters into the memory block pointed by the pointer. (ASCII code)
    [ : If the value of the memory block pointed to by the pointer is 0, then jump to the corresponding ']'; otherwise, continue to execute the next instruction.
    ] : If the value of the memory block pointed to by the pointer is not 0, then jump to the corresponding '['; otherwise, continue to execute the next instruction.
    The function returns 0 for successful execution and -1 for error.

  4. Declare a pointer to a location in the current data space.

Implement the loop statement in Brainfuck. For the [ command, it is necessary to record the current pointer position, and when the value stored in this position is 0, jump to the next ] command; for the ] command, it is necessary to return to the position of the previous [ command and execute again.

Simple code implementation:

#include <stdio.h>

int main(){
    
    
    char array[30000] = {
    
    0}; // 定义数据空间
    char input[1000]; // 存储 Brainfuck 源代码
    char *ptr = array; // 定义指针指向数据空间头部

    printf("请输入 Brainfuck 源代码:\n");
    fgets(input, 1000, stdin); // 读取用户输入的 Brainfuck 源代码

    for (char *p = input; *p; p++)
    {
    
    
        switch (*p)
        {
    
    
            case '>': ptr++; break;
            case '<': ptr--; break;
            case '+': (*ptr)++; break;
            case '-': (*ptr)--; break;
            case '.': putchar(*ptr); break;
            case ',': *ptr = getchar(); break;
            case '[':
            {
    
    
                char *tmp = p;
                while (*ptr)
                {
    
    
                    p++;
                    while (*p)
                    {
    
    
                        if (*p == '[') p++;
                        else if (*p == ']')
                        {
    
    
                            if (--tmp == p) break;
                            else p++;
                        }
                    }
                }
                break;
            }
            case ']':
            {
    
    
                char *tmp = p;
                while (*ptr)
                {
    
    
                    p--;
                    while (p >= input)
                    {
    
    
                        if (*p == ']') p--;
                        else if (*p == '[')
                        {
    
    
                            if (++tmp == p) break;
                            else p--;
                        }
                    }
                }
                break;
            }
        }
    }
    return 0;
}

In the above code, we define an array containing 30,000 characters as the data space of Brainfuck; then read the source code input by the user, and implement the 8 instructions of Brainfuck through the switch-case statement; finally define a pointer to point to the data space The head implements the loop statement.

It should be noted that the source code of Brainfuck is special, and some characters may be treated specially. Therefore, care should be taken during implementation, and characters that cannot be escaped need to be processed separately.

In addition to using a character array to simulate, we can also use structures to do some more complex implementations:

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include<string.h>

// Interpreter state
struct BFState {
    
    
    // The array and the size of the array.
    size_t array_len;
    uint8_t* array;

    // Pointer to the current position in the array; array <= cur < (array+array_len)
    uint8_t* cur;
};

//typedef unsigned char   uint8_t;

/*
parameter:
	state:
	一个指向 BFState 结构体的指针,该结构体记录了 brainfuck 程序的运行状态和数据。
	在函数内部,我们通过对 state 指针所引用的结构体进行操作,来实现 brainfuck 程序的运行。
	program:
	一个指向常量字符串的指针,该字符串表示要执行的 brainfuck 程序。
	在函数内部,我们需要解析该字符串并根据解析结果来执行各种指令,对state进行操作, 
	从而实现整个 brainfuck 程序的执行。
*/

// Return 0 on success, and -1 in case of an error (e.g., an out-of-bounds access).
int brainfuck(struct BFState* state, const char* program) {
    
    
    
    size_t program_len = 0; 	//brainfuck程序的长度 
    int ip = 0; 							// instruction pointer 程序指针 
	while(program[program_len] != '\0')	program_len++;
	
//	printf("%d\n%s\n", program_len, program);
	//扫描程序的每一个指令 
    while (ip < program_len) {
    
    
//    	printf("%c ", program[ip]);
        switch (program[ip]) {
    
    
            case '>':	// rightward shift
                if (state->cur + 1 >= state->array + state->array_len) {
    
    
                    return -1; // out of bounds
                }
                ip++; 
                state->cur++;
                break;
            case '<':	// leftward shift
                if (state->cur - 1 < state->array) {
    
    
                	printf("error here\n"); 
                    return -1; // out of bounds
                }
                ip++;
                state->cur--;
                break;
            case '+':	// plus 1
                (*(state->cur))++;
                ip++;
                break;
            case '-':	// minus 1
                (*(state->cur))--;
                ip++;
                break;
            case '.':	// output
                putchar(*state->cur);
                ip++;
                fflush(stdout); // ensure output is immediately written to the console
                break;
            case ',':	// Waits for input and stores the input character into the block of memory pointed to by the pointer
                *state->cur = getchar();
                ip++;
                break;
            case '[':
			    if ((*(state->cur)) == 0) {
    
     // move to the matching ]
					size_t nesting_level = 1;
					ip++;
			        while (ip < program_len) {
    
    
			            if (program[ip] == '[') nesting_level++;		//左括号 
						else if (program[ip] == ']') nesting_level--;	//右括号(抵消) 
			            if(nesting_level == 0) break;					//如果抵消之后为零,这个右括号就是与之匹配的 
			            ip++; 
			        }
			        if (ip == program_len) {
    
    
			            return -1; // no matching ]
			        }
			    }
			    ip++;
			    break;
			    
			case ']':
			    if ((*(state->cur)) != 0) {
    
     // move to the matching [
			        int nesting_level = 1;
			        ip--;
			        while (ip >= 0) {
    
    
			            if (program[ip] == ']')	nesting_level++;		//右括号
						else if (program[ip] == '[') nesting_level--;	//左括号(抵消)
			            if (nesting_level == 0) break;        			//如果抵消之后为零,这个右括号就是与之匹配的     
			            ip--;
			        }
			        if (ip < 0) {
    
    
			            return -1; // no matching [
			        }
			    }
			    ip++;
			    break;
            default:
                ip++; // invalid command
        }
    }

    return 0; // program successfully executed

}

int main(){
    
    
	size_t array_len = 3000;
    uint8_t* array = (uint8_t*)malloc(array_len*sizeof(uint8_t));
//    uint8_t* array = calloc(array_len, sizeof(uint8_t));
    if (!array) {
    
    
        fprintf(stderr, "could not allocate memory\n");
        return EXIT_FAILURE;
    }
	
	// 定义结构体,初始化结构体变量,指针指向首地址 
    struct BFState state = {
    
    
        .array_len = array_len,
        .array = array,
        .cur = array,
    };
    state.cur++;
    state.cur++;
    state.cur++;
    memset(state.array, 0, array_len);
//    state.array[0] = 1;
    
    int res = brainfuck(&state, "[[-<<+>>]<+[->+<]>>]<<<[>>[-<+>]<<[->>+<<]<]>>>");
	printf("res = %d\n", res);
	for(int i = 0; i < 50; i++) printf("%d ", state.array[i]);
	printf("\n");
	return 0;
}

//int main(int argc, char** argv) {
    
    
//    if (argc != 2) {
    
    
//        fprintf(stderr, "usage: %s <bfprog>\n", argv[0]);
//        return EXIT_FAILURE;
//    }
//
//    size_t array_len = 3000;
//    uint8_t* array = (uint8_t*)malloc(array_len*sizeof(uint8_t));
    uint8_t* array = calloc(array_len, sizeof(uint8_t));
//    if (!array) {
    
    
//        fprintf(stderr, "could not allocate memory\n");
//        return EXIT_FAILURE;
//    }
//	
//	// 定义结构体,初始化结构体变量,指针指向首地址 
//    struct BFState state = {
    
    
//        .array_len = array_len,
//        .array = array,
//        .cur = array,
//    };
//    int res = brainfuck(&state, argv[1]);
//    if (res) {
    
    
//        puts("an error occured");
//        return EXIT_FAILURE;
//    }
//
//    return EXIT_SUCCESS;
//}

In brainfuck language, programs perform calculations by manipulating data stored in data arrays. In the BFState structure, array points to the first address of the data array, and cur represents the current pointing position.

Because there are many instructions for manipulating data arrays in the brainfuck language, it is necessary to change the pointer of cur frequently. These instructions include "I" (pointer plus 1), "D" (pointer minus 1) and so on. The existence of the cur pointer allows the program to operate on the data array more conveniently, without having to start addressing from the first address of the array every time. In addition, cur can also be passed in different operations, realizing the multi-position operation of the data array.

Guess you like

Origin blog.csdn.net/z135733/article/details/130498982