About how I wrote the Brainfuck code generator

The other day I needed to write a solution to the problem of finding the maximum increasing sequence of digits in C.

However, I decided that solving this problem using the usual method would be boring and I decided to slightly complicate the task for myself, so that it would be more interesting. That's how the idea came up to write this code in brainfuck, and an interpreter for it in C.

Naturally, doing this with bare hands will be painful and unpleasant, so I decided to write a brainfuck code generator in Java.

A little bit about Brainfuck itself

Brainfck is an esoteric programming language, as the memory of the classic Brainfuck we have an array of 30,000 memory cells and the number of the selected cell (initially equal to zero), where each cell weighs one byte. And the usual syntax meets us with a sequence of the following characters: <>+-.,[]:

Symbols < And > change the index of the selected cell (index++ And index--)
Symbols + And - change the current value of the selected cell by +1 or -1 (buffer[index]++ And buffer[index]--)
Symbols , And . write the entered character to the cell value / output the cell value as a character (buffer[index] = getchar() And putchar(buffer[index]) )

However, the most interesting symbols are [ и ]they open/close a loop that will run until the value of the selected cell becomes 0, the important point is that this is checked at the beginning ([) и конце (]) loop, and if the value of the selected cell at the beginning of the loop was 0, then the interpreter will immediately jump to the end of the loop, skipping everything inside

Let's start coding

I created a class Brainfuck.java (by the way I wanted to call him Brainf$ck but I thought that it would be inconvenient to code like that) which would generate and simultaneously execute code, and I wrote the basic logic without cycles:

public class Brainfuck {

	private int[] buffer = new int[30000];
	public int minValue = 0, maxValue = 255; // Максимальное и минимальное значения ячеек
	private int index = 0;
	
	private CodeBuilder code = new CodeBuilder(); // Я создал CodeBuilder.java который иммет функционал StringBuilder-а, но с фозможность форматирования brainfuck кода
	protected boolean onlyCode = false; // Так как мы одновременно исполняем код и генерирум его, то будут моменты с циклами, когда создать код надо, а исполнять нет, ведь цикл будет пропущен в результате исполнения
	protected boolean shortVersion = false; // Для замены [-] на @ для того чтобы сделать код короче

	private StringBuilder output = new StringBuilder(); // Вывод результата исполнения Brainfuck-кода

	// Чтобы не вводить каждый раз с клавиатуры добавим возможность заранее заготовить input
	private String input = "";
	private int inputIndex = 0;
	public Brainfuck(String input) { 
		this.input = input;
	}

	// А теперь создадим базовые команды
	@Brainfucked // Команды для генерации кода у меня имеют эту антонацию
	private Brainfuck nextIndex() {
		code.append('>'); // Генерируем код
		index++; // И сразу его исполняем
		return this;
	}
	
	@Brainfucked
	private Brainfuck prevIndex() {
		code.append('<');
		index--;
		return this;
	}
	
	@Brainfucked
	public Brainfuck add() {
		code.append('+');
		if(!onlyCode) {
			maxMemoryUsed = Math.max(maxMemoryUsed, index); // Для дебага
			buffer[index]++;
			if(buffer[index] > maxValue) buffer[index] = minValue; // Переполнение
		}
		return this;
	}
	
	@Brainfucked
	public Brainfuck remove() {
		code.append('-');
		if(!onlyCode) {
			buffer[index]--;
			maxMemoryUsed = Math.max(maxMemoryUsed, index); // Для дебага
			if(buffer[index] < minValue) buffer[index] = maxValue; // Переполнение
		}
		return this;
	}

	@Brainfucked
	public Brainfuck read() {
		code.append(',');
		if(!onlyCode) {
			if(inputIndex >= input.length()) {
				input = scanner.nextLine() + "\n";
				inputIndex = 0;
			}
			buffer[index] = input.charAt(inputIndex++);
		}
		return this;
	}
	
	@Brainfucked
	public Brainfuck print() {
		code.append('.');
		if(!onlyCode) {
			output.append((char)buffer[index]);
		}
		return this;
	}
	
}

Let's add a couple more methods to make life easier:

@Brainfucked
public Brainfuck setIndex(int index) { // Будем выбирать нужный нам индекс в ячейке, автоматически добавляя нуное количество ">" и "<"
	while (index != this.index) {
		if(this.index > index) prevIndex();
		if(this.index < index) nextIndex();
	}
	return this;
}

@Brainfucked
public Brainfuck resetCell() { // [-] - сбрасывает значение в текущей ячейке, так как цикл постоянно уменьшает значение на -1, а при достижении нуля цикл прерывается
	code.append(shortVersion ? "@" : "[-]");
	if(!onlyCode) buffer[index] = 0;
	return this;
}

@Brainfucked // Аналогично setIndex добавляет нужное количество команд, но на этот раз это не смещение индекса, а изменение значения ячейки
public Brainfuck addValue(int value) {
	if(value == 0) return this;
	if(value > 0) {
		for (int i = 0; i < value; i++) add();
		return this;
	}
	for (int i = 0; i < -value; i++) remove();
	return this;
}

I also created many functions for debugging such as dump() (displays used memory cells with values ​​and variable names in the form of a table), comment(...) (adds a comment to the code), log(...) (output to console text if a piece of code is executed in brainfuck), you can look at them in more detail on the git.

Before implementing loops, let's create a system of variables
Although I have added the ability to create variables that occupy multiple memory cells, I have not yet implemented any actions with them.

public class Variable {

	public final String name; // Имя переменной, ни на что не влияет, нужно для дебагинга
	public final int size; // Размер переменной, иначе говоря сколько ячеек памяти будет занимать
	public final int index; // Индекс начальной ячейки, занимаямой переменной (я хотел сделать массив из индексов чтобы можно было пихать переменные в любое место, но в итоге отказался от этой идеи)
	protected final Brainfuck brainfuck; // Ссылка на генератор

	private boolean exist = true; // Переменную можно будет удалять, чтобы освободить место

	protected Variable(Brainfuck brainfuck, String name, int size, int index) {
		this.brainfuck = brainfuck;
		this.name = name.replace('.', '/');
		this.size = size;
		this.index = index;
		reset(); // Устанавливаем значение 0 при инициализации, чтобы избежать проблем в дальнейшем
		// "Дефолтные" переменные я буду начинать с "$", поэтому информацию об из объявлении не буду писать
		if(!name.startsWith("$")) Log.run("@ declarated at @", this.name, index);
	}

	@Brainfucked
	public void reset() { // Сбрасываем значение переменной
		if(isDeleted()) throw new DeletedVariableManipulation("change");
		select();
		brainfuck.resetCell();
	}
	
	@Brainfucked
	public void select() { // Выбираем ячеку с этой переменной
		if(isDeleted()) throw new DeletedVariableManipulation("select");
		brainfuck.setIndex(index);
	}
	
	@Brainfucked
	public void add(int i) { // Изменяем значение переменной
		if(isDeleted()) throw new DeletedVariableManipulation("change");
		select();
		brainfuck.addValue(i);
	}
	
	@Brainfucked
	public void delete() { // Удаление переменной
		if(isDeleted()) throw new DeletedVariableManipulation("delete already deleted");
		for (int i = 0; i < size; i++) {
			brainfuck.variables[index + i] = null;
		}
		exist = false;
	}
}

We will also create a type that will occupy 1 cell.
(Probably it would be better to call it Cell, considering that I can change the cell size, and Java has a default class with the same name, but what's done is done)

brainfuck.Byte.java:

public class Byte extends Variable {

	@Brainfucked
	public Byte(Brainfuck brainfuck, String name, int index) {
		super(brainfuck, name, 1, index);
	}

}

In the future, I will be describing many manipulations in Byte class

And in Barinfuck.java Let's write the following:

Variable[] variables = new Variable[buffer.length]; // Массив хранящий сущестующие переменные

@Brainfucked
public Variable variable(String name, int size) {
	// Поиск свободного места
	for (int i = 0; i < variables.length; i++) {
		if(variables[i] != null) continue;
		boolean free = true;
		for (int j = 0; j < size; j++) {
			if(variables[i+j] != null) {
				free = false;
				break;
			}
		}
		if(!free) continue;
		Variable variable = new Variable(this, name, size, i);
		for (int k = 0; k < size; k++) { // Занимаем место переменной
			variables[variable.index + k] = variable;
		}
		return variable;
	}
	throw new OutOfMemoryError("Not enoth memory for declarating varrible");
}

@Brainfucked
public Byte variable(String name) { // Анологично для переменной из одной ячейки
	for (int i = 0; i < variables.length; i++) {
		if(variables[i] != null) continue;
		Byte variable = new Byte(this, name, i);
		variables[variable.index] = variable;
		return variable;
	}
	throw new OutOfMemoryError("Not enoth memory for declarating varrible");
}

@Brainfucked
public Byte[] byteArray(String name, int size) { // Объявление массива
	for (int i = 0; i < variables.length; i++) {
		if(variables[i] != null) continue;
		boolean free = true;
		for (int j = 0; j < size; j++) {
			if(variables[i+j] != null) {
				free = false;
				break;
			}
		}
		if(!free) continue;
		Byte[] bs = new Byte[size];
		for (int k = 0; k < size; k++) {
			bs[k] = new Byte(this, name + "/" + k, i+k);
		}
		return bs;
	}
	throw new OutOfMemoryError("Not enoth memory for declarating array");
}

Now we can finally start creating the first cycle. If translated into Java, it will look like this:

while(var != 0) {
	...
	var--;
}

And here is the implementation in Java:

@Brainfucked
public void repeatWhile(Byte var, Runnable run) {
	var.select(); // Выбираем переменную для сравнения, т.к не гарантируется что выбрана именно ячейка переменной var
	CodeBuilder parent = code;
	parent.append('[');
	boolean runned = false;
	while (buffer[index] != 0 && !onlyCode) {
		code = new CodeBuilder();
		run.run();
		var.select(); // Снова выбираем переменную для сравнения, т.к "run" мог сместить индекс
		runned = true;
		if(!runnable) break;
	}

	if(!runned) {
		boolean oc = onlyCode;
		int ind = index;
		if(!var.toBoolean()) onlyCode = true; // Если ни ни одна итерация цикла не произошла, то тогда ключаем режим только генерации кода для блока внутри
		code = new CodeBuilder();
		var.select();
		run.run();
		var.select();
		onlyCode = oc; // Возвращаем режим к исходному состоянию
		if(!onlyCode) {
			index = ind;
		}
	}
	parent.append(code);
	parent.append(']');
	code = parent;
}

Great, the basic brainfuck commands are implemented, which means we can practically forget about brainfuck symbols and move on to more complex constructions:

The construction of copying a variable from one cell to another:

Unfortunately, you can't just write in brainfuck a = bfor this we even need a time cell, if we describe this algorithm in words it will be like this:

Сбросить значение текущей ячейки
Добавлять 1 к a и tmp b-раз (b используется в качестве счетчика и станет равным 0)
Добавлять 1 к b tmp-раз (tmp используется в качестве счетчика и станет равным 0)
Удалить tmp

Byte.java:

@Brainfucked
public void set(Byte var) {
	if(isDeleted()) throw new DeletedVariableManipulation("change");
	Byte tmp = brainfuck.variable("$bf/tmp/byte/copy"); // Создаем временную переменную
	reset(); // Сбрасываем текущее значение
	brainfuck.repeatWhile(var, () -> { // Добавляем к текущему значению и временнуму, ценой значения переменной var (она используется как счетчик цикла)
		add(1);
		tmp.add(1);
		var.add(-1);
	});
	brainfuck.repeatWhile(tmp, () -> { // Возвращаем значение в var ценой временной переменной
		var.add(1);
		tmp.add(-1);
	});
	tmp.delete(); // Удаляем временную переменную
}

On brainfuck it would look like this:

a[-]b[a+t+b-]t[b+t-]

(where instead of a, b, t there will be “>” and “<" in the quantity needed to move to these cells, in other words a means go to cell “a”)

Construction if x != 0

To turn the cycle into conditions, it is enough to reset the value of the selected cell to 0 or go to a cell with a value of 0, I chose the first option.

@Brainfucked
public void ifNotZero(Byte var, Runnable run) {
	Byte lock = variable("$bf/ifByte/circle");
	lock.set(var);
	repeatWhile(lock, () -> {
		run.run();
		lock.reset();
	});
	lock.delete();
}

On brainfuck it would look like this:

... // Копирование из var в lock
lock[
	... // Код в if
	lock[-]
]

Construction if x == 0 else

Now that we know how to do it if x != 0 we can create the opposite condition. For this we will need another temporary variable that will have the value 1, and when executed x != 0 change the value to 0, so the input to the second loop can be inverted

@Brainfucked
public void ifZeroElse(Byte var, Runnable runZero, Runnable runNoZero) {
	Byte inversed = variable("$bf/ifByte/circle");
	inversed.add(1);
	ifNotZero(var, () -> { // x != 0
		runNoZero.run();
		inversed.add(-1);
	});
	ifNotZero(inversed, runZero); // x == 0
	inversed.delete();
}

Construction if x == y else

The logic of this design is similar if x == 0 else but to get 0 we just subtract the value of y from x

if x < y else construct

This is the most complex of the implemented designs. To create it, we need an array of 6 cells, which can be created using the function byteArray(name, size)the array must be filled as follows:

Номер: 0 1 2 3 4 5 
Число: 0 1 0 x y 0

and then go to [3] array element.

The algorithm looks like this

{
	Вычитаем из [3] // ячейка и значальным значнием равным x
	Вычитаем из [4] // ячейка и значальным значнием равным y
	Если [4] != 0 {
		Сдвигаемся впрво на 1 ячейку
	}
	Сдвигаемся влево на 2 ячейки
}
Сдвигаемся влево на 1 ячейку
Если [текущая] != 0 { // если мы оказались на [1], т.е x >= y
	Вычитаем из [1] 
	... // Код если x >= y
	Переходим к [1]
}
Сдвигаемся влево на 1 ячейку
Если [текущая] != 0 { // если мы оказались на [0], т.е x < y
	Вычитаем из [0] 
	... // Код если x < y
	Переходим к [0]
}

Take a look at it x < y:

{
	Вычитаем из [3] // ячейка и значальным значнием равным x
	Вычитаем из [4] // ячейка и значальным значнием равным y
	Если [4] != 0 {
		Сдвигаемся впрво на 1 ячейку, мы на [5] // т.к x > y то [4] не станет никогда 0, т.к мы сбыстрее выйдем из цикла при [3] = 0
	}
	Сдвигаемся влево на 2 ячейкимы // Теперь мы на [3], но т.к x < y то [3] стало равной 0 быстрее, чем [4], а раз она 0 то цикл прерывается
}
Сдвигаемся влево на 1 ячейку // Мы на [2] ячейке
Если [текущая] != 0 { // сюда не заходим т.к [2] равно 0
	Вычитаем из [1] 
	... // Код если x >= y
	Переходим к [1]
}
Сдвигаемся влево на 1 ячейку // Мы на [1] ячейке
Если [текущая] != 0 { // а вот сюда заходим, ведь [1] равно 1
	Вычитаем из [0] 
	... // Код если x < y
	Переходим к [0]
}

Let's consider x >= y:

{
	Вычитаем из [3] // ячейка и значальным значнием равным x
	Вычитаем из [4] // ячейка и значальным значнием равным y
	Если [4] != 0 { // Это условие наступило т.к x >= y
		Сдвигаемся впрво на 1 ячейку // Теперь мы на [5]
	}
	Сдвигаемся влево на 2 ячейки // Теперь мы на [2], а она равна 0, что означает выход из цикла
}
Сдвигаемся влево на 1 ячейку // Теперь мы на [1]
Если [текущая] != 0 { // Условие выполняется, ведь [1] равно 1
	Вычитаем из [1] 
	... // Код если x >= y
	Переходим к [1]
}
Сдвигаемся влево на 1 ячейку // Теперь мы на [0]
Если [текущая] != 0 { // Условие не выполняется, ведь [0] равно 0
	Вычитаем из [0] 
	... // Код если x < y
	Переходим к [0]
}

In Java I wrote the following code:

@Brainfucked
public void ifLessThanElse(Byte x, Byte y, Runnable ifRun, Runnable elseRun) {
	Byte[] arr = byteArray("$bf/lessthan/array", 6);
	arr[1].add(1);
	arr[3].set(x);
	arr[4].set(y);
	arr[3].add(1);
	arr[4].add(1);
	arr[3].select();
	code.append("[->-[>]<<]"); // Первый цикл в brainfuck вариации
	code.append("<[-"); // Начло первого условия

	int min = Math.min(x.value(), y.value()) + 1; // Вместо прописывания логики цикла я просто прописал это вручную
	boolean less = x.value() < y.value();
	
	// Так как одно из них станет нулем, что создаст выход из цикла, я просто вычту минимум из x и y
	buffer[arr[3].index] -= min; 
	buffer[arr[4].index] -= min;
	
	// if x >= y блок
	boolean oc = onlyCode;
	onlyCode = less;
	index = arr[1].index; // Опять установлю вручную
	elseRun.run();
	arr[1].select();
	onlyCode = oc;
	
	// Конец первого и начало второго условия
	code.append("]<[-<");
	// if x < y блок
	oc = onlyCode;
	onlyCode = !less;
	index = arr[0].index; // Опять установлю вручную
	ifRun.run();
	arr[0].select();
	onlyCode = oc;
	code.append("]"); // Конец второго условия
	
	// Чистим память, занятую временным масивом
	for (int i = 0; i < arr.length; i++) {
		arr[i].delete();
	}
}

Brainfuck Mathematics

Now we will implement various actions in the Byte class. We will implement mathematics with two cells x And y

Multiplication x = x * y

Well, everything is simple here. We will need 2 temporary variables, in tmp0 we will transfer (not copy, but transfer) the value x. And then we will use tmp0 as a loop counter. Inside this loop we will add y with each iteration, so we will take y x times, which is multiplication. We will add y in a way similar to copying a variable, but this time we will not reset its value so as not to equate x y times, and add.

@Brainfucked
public void multiply(Byte mul) {
	if(isDeleted()) throw new DeletedVariableManipulation("change");
	select();
	Byte tmp0 = brainfuck.variable("$bf/varible/multiply/tmp0");
	Byte tmp1 = brainfuck.variable("$bf/varible/multiply/tmp1");
	brainfuck.repeatWhile(this, () -> { // переносим значение из this (x) в tmp1
		tmp1.add(1);
		add(-1);
	});
	brainfuck.repeatWhile(tmp1, () -> { // Повторяем x - раз
		// На этот раз reset() не нужен
		brainfuck.repeatWhile(mul, () -> {
			add(1);
			tmp0.add(1);
			mul.add(-1);
		});
		brainfuck.repeatWhile(tmp0, () -> {
			mul.add(1);
			tmp0.add(-1);
		});
		tmp1.add(-1);
	});
	tmp0.delete();
	tmp1.delete();
}

Division with remainder x + mod = x / y

The division is a bit more complicated. This time I needed as many as 4 temporary variables and one will be returned as the remainder.

mod – we will return it, this is our remainder
umod – it is necessary to calculate the remainder, it will be equal to y - modI will call it the inverse mod
div – here we will write the division result, and then we will transfer it to the cell x
lock – it will be a condition for the cycle and will initially be equal to 1so that we can set the value 0 and exit the loop
tmpDivider – we will copy y into it and use it as a loop counter

@Brainfucked
public Byte divide(@Final Byte divider) {
	Byte mod = brainfuck.variable("$bf/byte/divide/mod");
	Byte umod = brainfuck.variable("$bf/byte/divide/umod");
	Byte div = brainfuck.variable("$bf/byte/divide/div");
	Byte lock = brainfuck.variable("$bf/byte/divide/lock");
	Byte tmpDivider = brainfuck.variable("$bf/byte/divide/lock");
	lock.add(1);
	brainfuck.repeatWhile(lock, () -> {
		// Чтобы разделить мы должны посчитать сколько раз мы можем вычесть y
		div.add(1); // Увеличиваем счетчик
		brainfuck.ifZeroElse(this, () -> { // Если оказывается что x стал 0 то выходим из цикла
			lock.add(-1);
			umod.set(divider); // если х = 0 на этом этапе то значит что остаток от деления будет 0, поэтому в umod устанавливаем y
		}, () -> {
			tmpDivider.set(divider);
			tmpDivider.add(-1); // вычитаем еденицу, которую вернем после цикла, чтобы мы могли получить 0 внутри цикла
			brainfuck.repeatWhile(tmpDivider, () -> { // вычитаем по 1 (y-1) раз и если получаем 0 в процессе - выходим из цикла
				add(-1); // вичитаем по еденице
				tmpDivider.add(-1); // Уменьшаем счетчик цикла
				brainfuck.ifZero(this, () -> { // мы получили 0, значит деление завершено
					// Оставшееся значение счетцика будет обратным mod (y-mod)
					umod.add(1); 
					brainfuck.repeatWhile(tmpDivider, () -> {
						umod.add(1);
						tmpDivider.add(-1);
					});
					add(1); // возвращаем забранную еденицу
					lock.add(-1); // прерываем цикл
				});
			});
		});
		add(-1);
	});
	
	div.add(-1); // одно вычиание было не полным
	// Теперь посчитаем mod, зная обратный
	mod.set(divider);
	brainfuck.repeatWhile(umod, () -> {
		mod.add(-1);
		umod.add(-1);
	});
	// Перенесем из div (счетчика итераций) в this (х)
	reset();
	brainfuck.repeatWhile(div, () -> {
		add(1);
		div.add(-1);
	});
	// Удаляем временные переменные
	umod.delete();
	div.delete();
	lock.delete();
	tmpDivider.delete();
	// Возвращаем остаток от деления, обарите внимание, что пользователь должен будет отчищать память для него сам
	return mod;
}

BrainfuckIO – Input/Output

To simplify the input/output I created a class BrainfuckIO.java

Reading an integer

To read an integer we need 3 cells: variablewe will write the result into it and in we will read input symbols into it, as well as a temporary cell neednext she will be responsible for the cycle

We will read the symbols up to the stop symbol. First of all, we must read the first symbol and check if it is a minus, if so, then we will subtract from the cell variableand not add. Then we read the next symbol in incompare it to the stop symbol and if it is then we exit the loop, resetting neednextOtherwise, we will in 48 this is the numerical value of the symbol “0”, after which we will multiply variable on 10 and add/subtract value in:

@Brainfucked
public static void readInt(Byte variable, char stop) {
	variable.reset();
	Brainfuck bf = variable.brainfuck;
	Byte in = bf.$input();
	bf.read(in);
	bf.ifEqualElse(in, '-', () -> { // Если первый символ 0 то мы будем вычитать из variable
		Byte needNext = bf.variable("$bf/io/neednext");
		needNext.add(1);
		bf.repeatWhile(needNext, () -> {
			bf.read(in); // Читам еще один символ
			readAddInt$changeValue(bf, in, stop, needNext, variable, -1); // Передаем нужныее переменные и -1 как знак того что мы будем вычиаить
		});
		needNext.delete();
	}, () -> { // Если первый символ не 0 то мы будем прибавлять к variable
		Byte needNext = bf.variable("$bf/io/neednext");
		needNext.add(1);
		readAddInt$changeValue(bf, in, stop, needNext, variable, 1);
		bf.repeatWhile(needNext, () -> {
			bf.read(in); // Читам еще один символ
			readAddInt$changeValue(bf, in, stop, needNext, variable, 1);
		});
		needNext.delete();
	});
}

private static void readAddInt$changeValue(Brainfuck bf, Byte in, char stop, Byte needNext, Byte variable, int mul) { // Чтобы не код вынес его в функцию
	bf.ifEqualElse(in, stop, () -> { // Если считаный символ это стоп-символ то мы выходим из цикла путем установки neednext в 0
		needNext.add(-1); 
	}, () -> {
		in.add(-'0'); // Вычитаем 48 чтобы перевести символ в цифру
		variable.multiply(10); // Умножаем на 10 для разрядов
		bf.repeatWhile(in, () -> {
			in.add(-1); // in теперь счетчик цикла
			variable.add(mul); // добавляем/вычитаем 1 в variable
		});
	});
}

Output of the number

Well, and finally the most difficult thing, the code turned out to be very slow (brainfuck code), one of the reasons for this is the lack of a dynamic list relation, and I have to divide large numbers every time.

We'll need a bunch of cells:
value – the cell whose value we will output
v – for a copy value
out – cell for displaying a symbol
divider – for division to get a number from the desired rank
length – to store the number of output characters
i – copy length

First we need to calculate how many characters we need to output, for this we copy from the cell whose value we will output (let's call it value) into a temporary cell vnow we will divide value on 10 until we get zero, and after each iteration we will add 1 V lengthif the length works out 0then we will deduce 0otherwise we will length once do the following:

@Brainfucked
public static void printInt(Byte value) {
	Brainfuck bf = value.brainfuck;
	value.select();
	
	Byte length = bf.variable("$bf/io/printInt/length");
	Byte out = bf.variable("$bf/io/printInt/out");
	Byte v = bf.variable("$bf/io/printInt/v");
	Byte divider = bf.variable("$bf/io/printInt/divider");
	Byte i = bf.variable("$bf/io/printInt/i");
	v.set(value); // Зафиксируем value в v
	bf.repeatWhile(value, () -> { // Делим на 10 до тех пор пока value не станет равно 0
		value.divide(10).delete();
		length.add(1); // Считам количество делений
	});
	
	bf.ifEqualElse(length, 0, () -> { // Длина 0, значит выводим 0
		value.reset();
		value.print();
	}, () -> {
		value.set(v); // Копирум обратно
		bf.repeatWhile(length, () -> { // Выведем символы length раз
			i.set(length); // Надо для счета divider
			divider.reset();
			divider.add(1);
			i.add(-1);
			bf.repeatWhile(i, () -> { // Уножаем divider на десять (length-1) раз
				divider.multiply(10);
				i.add(-1);
			});
			out.set(value); // Копируем value в out чтобы потом разделить
			
			Byte mod = out.divide(divider); // Делим, нам нужен только остаток
			out.add('0'); // Переводим число в символ
			out.print(); // Выводим его
			value.set(mod); // Устанавливаем остаток в value
			mod.delete(); // Чистим за собой память
			length.add(-1); // Изменям счетчик цикла
		});
	});
	value.set(v); // Возвращаем value в прежнее состояние
	// Чистим память от временных ячеек
	length.delete(); 
	out.delete();
	v.delete();
	i.delete();
	divider.delete();
}

Now let's solve the problem

public class Brainfucker {

	public static Brainfuck brainfuck;

	public static void main(String[] args) throws Exception {
		String input = "7\n1 2 3 -1 3 4 8\n"; // пример входных значений
		
		brainfuck = new Brainfuck(input); // создаем генератор
		
		// Устанавливаем максим и минимум ячейки
		brainfuck.minValue = java.lang.Byte.MIN_VALUE; 
		brainfuck.maxValue = java.lang.Byte.MAX_VALUE;
		
		Byte length = brainfuck.variable("length"); // Сюда запишем длинну последовательности для чтения
		BrainfuckIO.readInt(length, '\n'); // Прочитаем первую строчку с единственным числом - длиной последовательности
		
		length.add(-2); // Первое число последовательности запишем в last, а последнее будет иметь стоп символ не ' ', а '\n'
		
		Byte last = brainfuck.variable("last"); // Предидущее число последовательности
		
		BrainfuckIO.readInt(last, ' '); // Читаем первое число последовательноти (после него идет ' ')
		
		Byte max = brainfuck.variable("max"); // Сюда будем записывать длину  максимальной последовательности из возрастающих чисел
		Byte count = brainfuck.variable("count"); // А это счетчик текущей длины последовательности из возрастающих чисел
		
		Byte a = brainfuck.variable("i"); // сюда будем записывать текущий элемент последовательности
		
		brainfuck.repeatWhile(length, () -> { // Повторим length раз
			logicForNextElement(a, ' ', last, count, max); // Обрбатываем элемент, т.к он не в кноце то стоп-символ это ' '
			last.set(a);
			length.add(-1);
		});
		
		logicForNextElement(a, '\n', last, count, max); // Обрбатываем элемент, т.к он в кноце то стоп-символ это '\n'
			
		BrainfuckIO.printInt(max); // Выводим результат
		
		String code = brainfuck.toString();
		Log.result(code + "\n"); // выводим сгенерированный brainfuck код в консоль
		
		Log.info(brainfuck.dump()); // Навяк выводим дамп brainfuck памяти (и результат вывода brainfuck программы)
		
	}
	
	private static void logicForNextElement(Byte a, char stopCharacter, Byte last, Byte count, Byte max) {
		BrainfuckIO.readInt(a, stopCharacter); // Читаем следующее число
		a.add(1); // Займем еденицу чтобы заменить условие "last < a" на "last <= a"
		brainfuck.ifLessThanElse(last, a, () -> { // if last < a
			count.add(1); // Круто, последовательность возрастает, давайте прибавим 1 к счетчику

			brainfuck.ifLessThanElse(count, max, () -> { // Проверим больше ли максимального значения значение счетчика
				// Если меньше то ничего не будем делать
			}, () -> {
				// А если больше то зададим максимальному значению, значения счетчика
				max.reset();
				max.set(count);
			});
		}, () ->; { // Эх, возрастание нарушено, сброим счетчик до 1
			count.reset();
			count.add(1);
		});
		a.add(-1); // Возвращаем занятую еденицу
	}
}

We launch it and get the following result:

[-]>[-]+>[-]>[-]<[-]<<[>>+>+<<<-]>>>[<<<+>>>-]<[<->[-]][-]>[-]<[-]<[>+>+<<-]>>[<<+>>-]<[[-]]<<[-]>[-],>[-]>[-]<+>>[-]<[-]<<[>>+>+<<<-]>>>[<<<+>>>-]<---------------------------------------------[<->[-]>[-]+>[-]>[-]<+>>[-]<[-]<<<<<[>>>>>+>+<<<<<<-]>>>>>>[<<<<<<+>>>>>>-]<----------[<->[-]<<<<<------------------------------------------------>>>>>>[-]++++++++++<<<<<<<>>>>>>>>[-]>[-]<<<<<<<<<[>>>>>>>>>+<<<<<<<<<-]>>>>>>>>>[<<[<<<<<<<+>>>>>>>>+<-]>[<+>-]>-]<<<<<<<<[-<+>]>>>>>]<[<->-]<[<<<,>>>>[-]>[-]<+>>[-]<[-]<<<<<[>>>>>+>+<<<<<<-]>>>>>>[<<<<<<+>>>>>>-]<----------[<->[-]<<<<<------------------------------------------------>>>>>>[-]++++++++++<<<<<<<>>>>>>>>[-]>[-]<<<<<<<<<[>>>>>>>>>+<<<<<<<<<-]>>>>>>>>>[<<[<<<<<<<+>>>>>>>>+<-]>[<+>-]>-]<<<<<<<<[-<+>]>>>>>]<[<->-]<]<]<[>>[-]+[<<<,>>>>[-]>[-]<+>>[-]<[-]<<<<<[>>>>>+>+<<<<<<-]>>>>>>[<<<<<<+>>>>>>-]<----------[<->[-]<<<<<------------------------------------------------>>>>>>[-]++++++++++<<<<<<<>>>>>>>>[-]>[-]<<<<<<<<<[>>>>>>>>>+<<<<<<<<<-]>>>>>>>>>[<<[<<<<<<<+>>>>>>>>+<-]>[<+>-]>-]<<<<<<<<[-<->]>>>>>]<[<->-]<]<<-]<<-->>[-][-]<,>>[-]>[-]<+>>[-]<[-]<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<---------------------------------------------[<->[-]>[-]+>[-]>[-]<+>>[-]<[-]<<<<<<[>>>>>>+>+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<--------------------------------[<->[-]<<<<<<------------------------------------------------>>>>>>>[-]++++++++++<<<<<<>>>>>>>[-]>[-]<<<<<<<<[>>>>>>>>+<<<<<<<<-]>>>>>>>>[<<[<<<<<<+>>>>>>>+<-]>[<+>-]>-]<<<<<<<<<[->+<]>>>>>>]<[<->-]<[<<<<,>>>>>[-]>[-]<+>>[-]<[-]<<<<<<[>>>>>>+>+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<--------------------------------[<->[-]<<<<<<------------------------------------------------>>>>>>>[-]++++++++++<<<<<<>>>>>>>[-]>[-]<<<<<<<<[>>>>>>>>+<<<<<<<<-]>>>>>>>>[<<[<<<<<<+>>>>>>>+<-]>[<+>-]>-]<<<<<<<<<[->+<]>>>>>>]<[<->-]<]<]<[>>[-]+[<<<<,>>>>>[-]>[-]<+>>[-]<[-]<<<<<<[>>>>>>+>+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<--------------------------------[<->[-]<<<<<<------------------------------------------------>>>>>>>[-]++++++++++<<<<<<>>>>>>>[-]>[-]<<<<<<<<[>>>>>>>>+<<<<<<<<-]>>>>>>>>[<<[<<<<<<+>>>>>>>+<-]>[<+>-]>-]<<<<<<<<<[->-<]>>>>>>]<[<->-]<]<<-][-]>[-]>[-]<<<<<[>>>>>[-]<<<<,>>>>>[-]>[-]<+>>[-]<[-]<<<<<<[>>>>>>+>+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<---------------------------------------------[<->[-]>[-]+>[-]>[-]<+>>[-]<[-]<<<<<<<<<[>>>>>>>>>+>+<<<<<<<<<<-]>>>>>>>>>>[<<<<<<<<<<+>>>>>>>>>>-]<--------------------------------[<->[-]<<<<<<<<<------------------------------------------------>>>>>>>>>>[-]++++++++++<<<<<<>>>>>>>[-]>[-]<<<<<<<<[>>>>>>>>+<<<<<<<<-]>>>>>>>>[<<[<<<<<<+>>>>>>>+<-]>[<+>-]>-]<<<<<<<<<<<<[->>>>+<<<<]>>>>>>>>>]<[<->-]<[<<<<<<<,>>>>>>>>[-]>[-]<+>>[-]<[-]<<<<<<<<<[>>>>>>>>>+>+<<<<<<<<<<-]>>>>>>>>>>[<<<<<<<<<<+>>>>>>>>>>-]<--------------------------------[<->[-]<<<<<<<<<------------------------------------------------>>>>>>>>>>[-]++++++++++<<<<<<>>>>>>>[-]>[-]<<<<<<<<[>>>>>>>>+<<<<<<<<-]>>>>>>>>[<<[<<<<<<+>>>>>>>+<-]>[<+>-]>-]<<<<<<<<<<<<[->>>>+<<<<]>>>>>>>>>]<[<->-]<]<]<[>>[-]+[<<<<<<<,>>>>>>>>[-]>[-]<+>>[-]<[-]<<<<<<<<<[>>>>>>>>>+>+<<<<<<<<<<-]>>>>>>>>>>[<<<<<<<<<<+>>>>>>>>>>-]<--------------------------------[<->[-]<<<<<<<<<------------------------------------------------>>>>>>>>>>[-]++++++++++<<<<<<>>>>>>>[-]>[-]<<<<<<<<[>>>>>>>>+<<<<<<<<-]>>>>>>>>[<<[<<<<<<+>>>>>>>+<-]>[<+>-]>-]<<<<<<<<<<<<[->>>>-<<<<]>>>>>>>>>]<[<->-]<]<<-]<+>[-]>[-]>[-]>[-]>[-]>[-]<<<<+<[-]>>>[-]<<<<<<<[>>>>>>>+<<<+<<<<-]>>>>[<<<<+>>>>-][-]>>>>[-]<<<<<[>>>>>+<<<<+<-]>[<+>-]>>>+>+<[->-[>]<<]<[-<<<[-]+>>>]<[-<<<+>>[-]>[-]>[-]>[-]>[-]>[-]<<<<+<[-]>>>[-]<<<<<[>>>>>+<<<+<<-]>>[<<+>>-][-]>>>>[-]<<<<<<<[>>>>>>>+<<<<+<<<-]>>>[<<<+>>>-]>>>+>+<[->-[>]<<]<[-<<<<[-]>>>[-]<<<[-]>[<+>>>+<<-]>>[<<+>>-]>]<[-<]]<->[-]<<<<[-]>>>[<<<+>>>>+<-]>[<+>-]<<<<<<-]>>>>>[-]<<<<,>>>>>[-]>[-]<+>>[-]<[-]<<<<<<[>>>>>>+>+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<---------------------------------------------[<->[-]>[-]+>[-]>[-]<+>>[-]<[-]<<<<<<<<<[>>>>>>>>>+>+<<<<<<<<<<-]>>>>>>>>>>[<<<<<<<<<<+>>>>>>>>>>-]<----------[<->[-]<<<<<<<<<------------------------------------------------>>>>>>>>>>[-]++++++++++<<<<<<>>>>>>>[-]>[-]<<<<<<<<[>>>>>>>>+<<<<<<<<-]>>>>>>>>[<<[<<<<<<+>>>>>>>+<-]>[<+>-]>-]<<<<<<<<<<<<[->>>>+<<<<]>>>>>>>>>]<[<->-]<[<<<<<<<,>>>>>>>>[-]>[-]<+>>[-]<[-]<<<<<<<<<[>>>>>>>>>+>+<<<<<<<<<<-]>>>>>>>>>>[<<<<<<<<<<+>>>>>>>>>>-]<----------[<->[-]<<<<<<<<<------------------------------------------------>>>>>>>>>>[-]++++++++++<<<<<<>>>>>>>[-]>[-]<<<<<<<<[>>>>>>>>+<<<<<<<<-]>>>>>>>>[<<[<<<<<<+>>>>>>>+<-]>[<+>-]>-]<<<<<<<<<<<<[->>>>+<<<<]>>>>>>>>>]<[<->-]<]<]<[>>[-]+[<<<<<<<,>>>>>>>>[-]>[-]<+>>[-]<[-]<<<<<<<<<[>>>>>>>>>+>+<<<<<<<<<<-]>>>>>>>>>>[<<<<<<<<<<+>>>>>>>>>>-]<----------[<->[-]<<<<<<<<<------------------------------------------------>>>>>>>>>>[-]++++++++++<<<<<<>>>>>>>[-]>[-]<<<<<<<<[>>>>>>>>+<<<<<<<<-]>>>>>>>>[<<[<<<<<<+>>>>>>>+<-]>[<+>-]>-]<<<<<<<<<<<<[->>>>-<<<<]>>>>>>>>>]<[<->-]<]<<-]<+>[-]>[-]>[-]>[-]>[-]>[-]<<<<+<[-]>>>[-]<<<<<<<[>>>>>>>+<<<+<<<<-]>>>>[<<<<+>>>>-][-]>>>>[-]<<<<<[>>>>>+<<<<+<-]>[<+>-]>>>+>+<[->-[>]<<]<[-<<<[-]+>>>]<[-<<<+>>[-]>[-]>[-]>[-]>[-]>[-]<<<<+<[-]>>>[-]<<<<<[>>>>>+<<<+<<-]>>[<<+>>-][-]>>>>[-]<<<<<<<[>>>>>>>+<<<<+<<<-]>>>[<<<+>>>-]>>>+>+<[->-[>]<<]<[-<<<<[-]>>>[-]<<<[-]>[<+>>>+<<-]>>[<<+>>-]>]<[-<]]<-<<>>>[-]>[-]>[-]>[-]>[-]>[-]<<<[-]<<<<<[>>>>>+>>>+<<<<<<<<-]>>>>>>>>[<<<<<<<<+>>>>>>>>-]<<<<<<<<[>>>>>>>>[-]++++++++++>[-]>[-]>[-]>[-]>[-]<+[<+>>>[-]+>[-]>[-]<[-]<<<<<<<<<<<<<<<[>>>>>>>>>>>>>>>+>+<<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>[<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>-]<[>[-]<<<[-]<<<<<[>>>>>+>>>+<<<<<<<<-]>>>>>>>>[<<<<<<<<+>>>>>>>>-]<<<-[<<<<<<<<<<<<<->>>>>>>>>>>>>->>>[-]+>[-]>[-]<[-]<<<<<<<<<<<<<<<<<[>>>>>>>>>>>>>>>>>+>+<<<<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>>>>[<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>-]<[<->[-]][-]>[-]<[-]<[>+>+<<-]>>[<<+>>-]<[<<<<<<<+>>>[<<<+>>>-]<<<<<<<<<<<<<+>>>>>>>>>>>>->>>>>[-]]<<<<]>->[-]][-]>[-]<[-]<[>+>+<<-]>>[<<+>>-]<[<<<->>>>[-]<<<<<<[-]<<[>>+>>>>>>+<<<<<<<<-]>>>>>>>>[<<<<<<<<+>>>>>>>>-]<[-]]<<<<<<<<<<<<<<<->>>>>>>>>>>>]<->>>[-]<<<<<[-]<[>+>>>>>+<<<<<<-]>>>>>>[<<<<<<+>>>>>>-]<<<<[<->-]<<<<<<<<<<[-]>>>>>>>>>>>[<<<<<<<<<<<+>>>>>>>>>>>-]<<<<<<<<+<<<]>>>>>>>>[-]>[-]<+>>[-]<[-]<<<<<<[>>>>>>+>+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<[<->[-]>[-]<<<<<<<<<<[-]>>>>>[<<<<<+>>>>>>>>>>+<<<<<-]>>>>>[<<<<<+>>>>>-]<<<<<<<[>>>>>>>[-]<<<[-]<<<<[>>>>+>>>+<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<<<<[-]+>-[>>>[-]++++++++++<<<<>>>>>[-]>[-]<<<<<<[>>>>>>+<<<<<<-]>>>>>>[<<[<<<<+>>>>>+<-]>[<+>-]>-]<<<<<-]>>>[-]<<<<<<[-]<<<<[>>>>+>>>>>>+<<<<<<<<<<-]>>>>>>>>>>[<<<<<<<<<<+>>>>>>>>>>-][-]>[-]>[-]>[-]>[-]<+[<+>>>[-]+>[-]>[-]<[-]<<<<<<<<<<<<[>>>>>>>>>>>>+>+<<<<<<<<<<<<<-]>>>>>>>>>>>>>[<<<<<<<<<<<<<+>>>>>>>>>>>>>-]<[>[-]<<<[-]<<<<<<<<[>>>>>>>>+>>>+<<<<<<<<<<<-]>>>>>>>>>>>[<<<<<<<<<<<+>>>>>>>>>>>-]<<<-[<<<<<<<<<<->>>>>>>>>>->>>[-]+>[-]>[-]<[-]<<<<<<<<<<<<<<[>>>>>>>>>>>>>>+>+<<<<<<<<<<<<<<<-]>>>>>>>>>>>>>>>[<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>-]<[<->[-]][-]>[-]<[-]<[>+>+<<-]>>[<<+>>-]<[<<<<<<<+>>>[<<<+>>>-]<<<<<<<<<<+>>>>>>>>>->>>>>[-]]<<<<]>->[-]][-]>[-]<[-]<[>+>+<<-]>>[<<+>>-]<[<<<->>>>[-]<<<<<<[-]<<<<<[>>>>>+>>>>>>+<<<<<<<<<<<-]>>>>>>>>>>>[<<<<<<<<<<<+>>>>>>>>>>>-]<[-]]<<<<<<<<<<<<->>>>>>>>>]<->>>[-]<<<<<[-]<<<<[>>>>+>>>>>+<<<<<<<<<-]>>>>>>>>>[<<<<<<<<<+>>>>>>>>>-]<<<<[<->-]<<<<<<<[-]>>>>>>>>[<<<<<<<<+>>>>>>>>-]<<<<<<<<++++++++++++++++++++++++++++++++++++++++++++++++.>>>>>>>[-]<<<<<<<<<<<[-]>>>>>>>>>>[<<<<<<<<<<+>>>>>>>>>>>+<-]>[<+>-]<<<<<<<<-]>>>>>>]<[<<<<<<<<[-].>>>>>>>>-][-]<<<<<<<<[-]>>>>>[<<<<<+>>>>>>>>+<<<-]>>>[<<<+>>>-]

Index: 11
Memory (22/3000 bytes):
#0 	#1   #2   #3  #4  #5  #6  #7   #8  #9
[0] [10] [-3] [4] [4] [8] [0] [52] [4] [1]
[ ] [\n] [ ]  [ ] [ ] [ ] [ ] [4]  [ ] [ ]
 l   $    l    m   c   i
 e   i    a    a   o
 n   n    s    x   u
 g   p    t    n
 t   u    t
 h   t
Result: 4

Let's copy the code and check it on this wonderful site:
https://esolangpark.vercel.app/ide/brainfuck

result of executing brainfuck code on the site

result of executing brainfuck code on the site

C interpreter

Cool, it works, now let's translate this into variables in C, where the repetitions from +-<> characters will be removed and the number of repetitions will be in the array repeatswe get the following:

char commands[] = "@>@+>@>@<@<[>+>+<-]>[<+>-]<[<->@]@>@<@<[>+>+<-]>[<+>-]<[@]<@>@,>@>@<+>@<@<[>+>+<-]>[<+>-]<-[<->@>@+>@>@<+>@<@<[>+>+<-]>[<+>-]<-[<->@<->@+<>@>@<[>+<-]>[<[<+>+<-]>[<+>-]>-]<[-<+>]>]<[<->-]<[<,>@>@<+>@<@<[>+>+<-]>[<+>-]<-[<->@<->@+<>@>@<[>+<-]>[<[<+>+<-]>[<+>-]>-]<[-<+>]>]<[<->-]<]<]<[>@+[<,>@>@<+>@<@<[>+>+<-]>[<+>-]<-[<->@<->@+<>@>@<[>+<-]>[<[<+>+<-]>[<+>-]>-]<[-<->]>]<[<->-]<]<-]<->@@<,>@>@<+>@<@<[>+>+<-]>[<+>-]<-[<->@>@+>@>@<+>@<@<[>+>+<-]>[<+>-]<-[<->@<->@+<>@>@<[>+<-]>[<[<+>+<-]>[<+>-]>-]<[->+<]>]<[<->-]<[<,>@>@<+>@<@<[>+>+<-]>[<+>-]<-[<->@<->@+<>@>@<[>+<-]>[<[<+>+<-]>[<+>-]>-]<[->+<]>]<[<->-]<]<]<[>@+[<,>@>@<+>@<@<[>+>+<-]>[<+>-]<-[<->@<->@+<>@>@<[>+<-]>[<[<+>+<-]>[<+>-]>-]<[->-<]>]<[<->-]<]<-]@>@>@<[>@<,>@>@<+>@<@<[>+>+<-]>[<+>-]<-[<->@>@+>@>@<+>@<@<[>+>+<-]>[<+>-]<-[<->@<->@+<>@>@<[>+<-]>[<[<+>+<-]>[<+>-]>-]<[->+<]>]<[<->-]<[<,>@>@<+>@<@<[>+>+<-]>[<+>-]<-[<->@<->@+<>@>@<[>+<-]>[<[<+>+<-]>[<+>-]>-]<[->+<]>]<[<->-]<]<]<[>@+[<,>@>@<+>@<@<[>+>+<-]>[<+>-]<-[<->@<->@+<>@>@<[>+<-]>[<[<+>+<-]>[<+>-]>-]<[->-<]>]<[<->-]<]<-]<+>@>@>@>@>@>@<+<@>@<[>+<+<-]>[<+>-]@>@<[>+<+<-]>[<+>-]>+>+<[->-[>]<]<[-<@+>]<[-<+>@>@>@>@>@>@<+<@>@<[>+<+<-]>[<+>-]@>@<[>+<+<-]>[<+>-]>+>+<[->-[>]<]<[-<@>@<@>[<+>+<-]>[<+>-]>]<[-<]]<->@<@>[<+>+<-]>[<+>-]<-]>@<,>@>@<+>@<@<[>+>+<-]>[<+>-]<-[<->@>@+>@>@<+>@<@<[>+>+<-]>[<+>-]<-[<->@<->@+<>@>@<[>+<-]>[<[<+>+<-]>[<+>-]>-]<[->+<]>]<[<->-]<[<,>@>@<+>@<@<[>+>+<-]>[<+>-]<-[<->@<->@+<>@>@<[>+<-]>[<[<+>+<-]>[<+>-]>-]<[->+<]>]<[<->-]<]<]<[>@+[<,>@>@<+>@<@<[>+>+<-]>[<+>-]<-[<->@<->@+<>@>@<[>+<-]>[<[<+>+<-]>[<+>-]>-]<[->-<]>]<[<->-]<]<-]<+>@>@>@>@>@>@<+<@>@<[>+<+<-]>[<+>-]@>@<[>+<+<-]>[<+>-]>+>+<[->-[>]<]<[-<@+>]<[-<+>@>@>@>@>@>@<+<@>@<[>+<+<-]>[<+>-]@>@<[>+<+<-]>[<+>-]>+>+<[->-[>]<]<[-<@>@<@>[<+>+<-]>[<+>-]>]<[-<]]<-<>@>@>@>@>@>@<@<[>+>+<-]>[<+>-]<[>@+>@>@>@>@>@<+[<+>@+>@>@<@<[>+>+<-]>[<+>-]<[>@<@<[>+>+<-]>[<+>-]<-[<->->@+>@>@<@<[>+>+<-]>[<+>-]<[<->@]@>@<@<[>+>+<-]>[<+>-]<[<+>[<+>-]<+>->@]<]>->@]@>@<@<[>+>+<-]>[<+>-]<[<->@<@<[>+>+<-]>[<+>-]<@]<->]<->@<@<[>+>+<-]>[<+>-]<[<->-]<@>[<+>-]<+<]>@>@<+>@<@<[>+>+<-]>[<+>-]<[<->@>@<@>[<+>+<-]>[<+>-]<[>@<@<[>+>+<-]>[<+>-]<@+>-[>@+<>@>@<[>+<-]>[<[<+>+<-]>[<+>-]>-]<-]>@<@<[>+>+<-]>[<+>-]@>@>@>@>@<+[<+>@+>@>@<@<[>+>+<-]>[<+>-]<[>@<@<[>+>+<-]>[<+>-]<-[<->->@+>@>@<@<[>+>+<-]>[<+>-]<[<->@]@>@<@<[>+>+<-]>[<+>-]<[<+>[<+>-]<+>->@]<]>->@]@>@<@<[>+>+<-]>[<+>-]<[<->@<@<[>+>+<-]>[<+>-]<@]<->]<->@<@<[>+>+<-]>[<+>-]<[<->-]<@>[<+>-]<+.>@<@>[<+>+<-]>[<+>-]<-]>]<[<@.>-]@<@>[<+>+<-]>[<+>-]";
char repeats[] = { 1,1,1,1,1,2,2,1,1,1,3,1,3,3,1,3,1,1,1,1,1,1,1,1,1,1,1,1,2,1,2,2,1,2,1,1,2,1,1,1,1,1,2,1,2,2,1,1,1,3,1,3,3,1,3,1,1,45,1,1,1,1,1,1,1,1,1,2,1,5,5,1,1,1,6,1,6,6,1,6,1,1,10,1,1,1,5,48,6,10,7,8,1,9,9,1,9,1,9,2,7,1,8,1,1,1,1,1,1,1,1,1,1,8,1,1,1,1,5,1,1,1,1,1,1,3,4,1,1,1,2,1,5,5,1,1,1,6,1,6,6,1,6,1,1,10,1,1,1,5,48,6,10,7,8,1,9,9,1,9,1,9,2,7,1,8,1,1,1,1,1,1,1,1,1,1,8,1,1,1,1,5,1,1,1,1,1,1,1,1,2,1,3,4,1,1,1,2,1,5,5,1,1,1,6,1,6,6,1,6,1,1,10,1,1,1,5,48,6,10,7,8,1,9,9,1,9,1,9,2,7,1,8,1,1,1,1,1,1,1,1,1,1,8,1,1,1,1,5,1,1,1,1,1,1,2,1,2,2,2,1,2,1,1,1,2,1,3,3,1,1,1,4,1,4,4,1,4,1,1,45,1,1,1,1,1,1,1,1,1,2,1,6,6,1,1,1,7,1,7,7,1,7,1,1,32,1,1,1,6,48,7,10,6,7,1,8,8,1,8,1,8,2,6,1,7,1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,6,1,1,1,1,1,1,4,5,1,1,1,2,1,6,6,1,1,1,7,1,7,7,1,7,1,1,32,1,1,1,6,48,7,10,6,7,1,8,8,1,8,1,8,2,6,1,7,1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,6,1,1,1,1,1,1,1,1,2,1,4,5,1,1,1,2,1,6,6,1,1,1,7,1,7,7,1,7,1,1,32,1,1,1,6,48,7,10,6,7,1,8,8,1,8,1,8,2,6,1,7,1,1,1,1,1,1,1,1,1,1,9,1,1,1,1,6,1,1,1,1,1,1,2,1,1,1,5,5,4,5,1,1,1,2,1,6,6,1,1,1,7,1,7,7,1,7,1,1,45,1,1,1,1,1,1,1,1,1,2,1,9,9,1,1,1,10,1,10,10,1,10,1,1,32,1,1,1,9,48,10,10,6,7,1,8,8,1,8,1,8,2,6,1,7,1,1,1,1,1,1,1,1,1,1,12,1,4,1,4,9,1,1,1,1,1,1,7,8,1,1,1,2,1,9,9,1,1,1,10,1,10,10,1,10,1,1,32,1,1,1,9,48,10,10,6,7,1,8,8,1,8,1,8,2,6,1,7,1,1,1,1,1,1,1,1,1,1,12,1,4,1,4,9,1,1,1,1,1,1,1,1,2,1,7,8,1,1,1,2,1,9,9,1,1,1,10,1,10,10,1,10,1,1,32,1,1,1,9,48,10,10,6,7,1,8,8,1,8,1,8,2,6,1,7,1,1,1,1,1,1,1,1,1,1,12,1,4,1,4,9,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,4,1,1,3,7,7,1,3,1,4,1,4,4,1,4,1,4,5,5,1,4,1,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,2,1,1,3,1,3,1,1,3,1,2,1,1,1,1,1,4,1,1,3,5,5,1,3,1,2,1,2,2,1,2,1,4,7,7,1,4,1,3,1,3,3,1,3,1,3,1,1,1,1,1,1,1,1,2,1,1,4,3,3,1,1,1,3,1,2,1,2,2,1,2,1,1,1,1,1,1,1,1,4,3,3,1,4,1,1,1,1,1,1,1,1,6,1,5,4,5,1,1,1,2,1,6,6,1,1,1,7,1,7,7,1,7,1,1,45,1,1,1,1,1,1,1,1,1,2,1,9,9,1,1,1,10,1,10,10,1,10,1,1,10,1,1,1,9,48,10,10,6,7,1,8,8,1,8,1,8,2,6,1,7,1,1,1,1,1,1,1,1,1,1,12,1,4,1,4,9,1,1,1,1,1,1,7,8,1,1,1,2,1,9,9,1,1,1,10,1,10,10,1,10,1,1,10,1,1,1,9,48,10,10,6,7,1,8,8,1,8,1,8,2,6,1,7,1,1,1,1,1,1,1,1,1,1,12,1,4,1,4,9,1,1,1,1,1,1,1,1,2,1,7,8,1,1,1,2,1,9,9,1,1,1,10,1,10,10,1,10,1,1,10,1,1,1,9,48,10,10,6,7,1,8,8,1,8,1,8,2,6,1,7,1,1,1,1,1,1,1,1,1,1,12,1,4,1,4,9,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,4,1,1,3,7,7,1,3,1,4,1,4,4,1,4,1,4,5,5,1,4,1,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,2,1,1,3,1,3,1,1,3,1,2,1,1,1,1,1,4,1,1,3,5,5,1,3,1,2,1,2,2,1,2,1,4,7,7,1,4,1,3,1,3,3,1,3,1,3,1,1,1,1,1,1,1,1,2,1,1,4,3,3,1,1,1,3,1,2,1,2,2,1,2,1,1,1,1,1,1,1,2,3,1,1,1,1,1,3,5,5,1,3,1,8,1,8,8,1,8,1,8,8,10,1,1,1,1,1,1,1,1,1,3,1,1,1,1,15,15,1,1,1,16,1,16,16,1,16,1,1,1,3,5,5,1,3,1,8,1,8,8,1,8,1,3,1,13,1,13,1,3,1,1,1,1,17,17,1,1,1,18,1,18,18,1,18,1,1,1,1,1,1,1,1,1,1,1,1,2,1,2,2,1,2,1,1,7,1,3,3,1,3,1,13,1,12,1,5,4,1,1,1,1,1,1,1,1,1,1,2,1,2,2,1,2,1,1,3,1,4,6,2,2,1,6,1,8,1,8,8,1,8,1,1,15,1,12,1,1,3,5,1,1,1,5,1,6,1,6,6,1,6,1,4,1,1,1,1,10,11,11,1,11,1,8,1,3,8,1,1,1,2,1,6,6,1,1,1,7,1,7,7,1,7,1,1,1,1,1,1,10,5,5,1,10,1,5,1,5,5,1,5,1,7,7,3,4,4,1,3,1,7,1,7,7,1,7,1,4,1,1,1,3,10,4,5,1,6,6,1,6,1,6,2,4,1,5,1,1,1,1,1,1,1,1,1,1,5,1,3,6,4,4,1,6,1,10,1,10,10,1,10,1,1,1,1,1,1,1,1,1,3,1,1,1,1,12,12,1,1,1,13,1,13,13,1,13,1,1,1,3,8,8,1,3,1,11,1,11,11,1,11,1,3,1,10,1,10,1,3,1,1,1,1,14,14,1,1,1,15,1,15,15,1,15,1,1,1,1,1,1,1,1,1,1,1,1,2,1,2,2,1,2,1,1,7,1,3,3,1,3,1,10,1,9,1,5,4,1,1,1,1,1,1,1,1,1,1,2,1,2,2,1,2,1,1,3,1,4,6,5,5,1,6,1,11,1,11,11,1,11,1,1,12,1,9,1,1,3,5,4,4,1,5,1,9,1,9,9,1,9,1,4,1,1,1,1,7,8,8,1,8,1,8,48,7,11,10,10,1,11,1,1,1,1,1,1,1,1,8,1,6,1,8,8,1,8,5,5,1,8,1,3,1,3,3,1,3,1 };

Let's allocate memory for executing the brainfuck code, our version of the interpreter will have a larger bit depth so that it can work with large numbers:

#define BUFFER_SIZE 220

short int buffer[BUFFER_SIZE];
for (int i = 0; i < BUFFER_SIZE; i++) {
	buffer[i] = 0;
}

If the teams +-<>,. are as simple as possible to implement, then for implementation [] we'll have to work a little: we'll create an array in which we'll store the indices of the pairs of brackets and the array index repeats for each bracket:

unsigned int openi[sizeof(commands)];
unsigned int openr[sizeof(commands)];

To define indices, let's create a stack:

int stack[10000]; // Тут будем хранить индексы команд
int rstack[10000]; // Тут будем хранить индексы повторов
int top = -1;

Now let's fill the arrays openi And openr:

int repatIndex = 0;
for (int i = 0; i < sizeof(commands); i++) {
	if (commands[i] == ']') {
		 // Забираем последний элемент из стека и записывам результаты в массивы индексов и повторений
		int openIndex = stack[top];
		int openRs = rstack[top];
		top--;
		openi[i] = openIndex;
		openr[i] = openRs;
		openi[openIndex] = i;
		openr[openIndex] = rs;
		continue;
	}
	openi[i] = 0;
	openr[i] = 0;
	if (commands[i] == '[') {
		// Добавляем в стек индекс и индекс повторения
		top++;
		stack[top] = i;
		rstack[top] = rs;
		continue;
	}
	if (commands[i] == '+' || commands[i] == '-' || commands[i] == '>' || commands[i] == '<') { // Если символ поддерживает повторение то смещаем индекс поторения
		rs++;
		openr[i] = rs;
		repatIndex++;
		continue;
	}
}

Now that we can easily navigate to a specific element of the array, let's write some code that will iterate through the brainfuck commands:

int index = 0;  // Индек указывающий на выбранную ячейку памяти

char c;
unsigned int i = 0; // Индекс для чтения массива команд
unsigned int ri = 0; // Индекс для чтения массива повторений

while (1) {
	c = commands[i];
	if (c == '@') { // Наш специальный символ для обнуления массива, он эпителиален конструкции [-]
		buffer[index] = 0; 
	} else if (c == '>') { // Символ смещающий индекс ячейки (вправо)
		index+= repeats[ri]; // Смещаемм индекс на количество подряд идущих символов в сиходном коде
		ri++;
	} else if (c == '<') { // Символ смещающий индекс ячейки (влево)
		index-=repeats[ri]; // Смещаемм индекс на количество подряд идущих символов в сиходном коде
		ri++;
	} else if (c == '+') { // Символ изменения значения ячейки (добавление 1)
		buffer[index]+= repeats[ri]; // Изменяем значение на количество подряд идущих символов в сиходном коде
		ri++;
	} else if (c == '-') { // Символ изменения значения ячейки (вычитание 1)
		buffer[index]-= repeats[ri]; // Изменяем значение на количество подряд идущих символов в сиходном коде
		ri++;
	} else if (c == '.') { // Выводим значение ячейки в символьном виде
		printf("%c", buffer[index]);
	} else if (c == ',') { // Читаем значение в ячейку
		char read = 0;
		scanf_s("%c", &read);
		buffer[index] = read;
	} else if (c == '[') { // Начало цикла
		if (buffer[index] == 0) { // Если значение 0 то сразу переходи к паре
			int d = 0;
			ri = openr[i];
			i = openi[i];
			i++;
			continue;
		}
	} else if (c == ']') {
		if (buffer[index] != 0) { // Если значение не 0 то сразу переходи к парной открывающей скобке
			ri = openr[i];
			i = openi[i]; // jump to pair - '['
			continue; // prevent i++
		}
	}
	if (i < 0) {
		printf("ERROR i < 0: %d", i);
		break;
	}
	i++;
	if (i >= strlen(commands)) break;
}

The resulting code was as follows:

#include 
#include 
#include 
#include 

#define BUFFER_SIZE 220

char commands[] = ...;
char repeats[] = {...};

unsigned int openi[sizeof(commands)];
unsigned int openr[sizeof(commands)];

void init() {
	int stack[10000];
	int rstack[10000];
	int top = -1;
	int rs = 0;
	int repatIndex = 0;
	for (int i = 0; i < sizeof(commands); i++) {
		if (commands[i] == ']') {
			int openIndex = stack[top];
			int openRs = rstack[top];
			top--;
			openi[i] = openIndex;
			openr[i] = openRs;
			openi[openIndex] = i;
			openr[openIndex] = rs;
			continue;
		}
		openi[i] = 0;
		openr[i] = 0;
		if (commands[i] == '[') {
			top++;
			stack[top] = i;
			rstack[top] = rs;
			continue;
		}
		if (commands[i] == '+' || commands[i] == '-' || commands[i] == '>' || commands[i] == '<') {
			rs++;
			openr[i] = rs;
			repatIndex++;
			continue;
		}
	}
}

int main() {
	init();
	short int buffer[BUFFER_SIZE];
	for (int i = 0; i < BUFFER_SIZE; i++) {
		buffer[i] = 0;
	}
	int index = 0;

	char c;
	unsigned int i = 0;
	unsigned int ri = 0;

	while (1) {
		c = commands[i];
		if (c == '@') {
			buffer[index] = 0;
		} else if (c == '>') {
			index+= repeats[ri];
			ri++;
		} else if (c == '<') {
			index-=repeats[ri];
			ri++;
		} else if (c == '+') {
			buffer[index]+= repeats[ri];
			ri++;
		} else if (c == '-') {
			buffer[index]-= repeats[ri];
			ri++;
		} else if (c == '.') {
			printf("%c", buffer[index]);
		} else if (c == ',') {
			char read = 0;
			scanf_s("%c", &read);
			buffer[index] = read;
		} else if (c == '[') {
			if (buffer[index] == 0) {
				ri = openr[i];
				i = openi[i];
				i++;
				continue;
			}
		} else if (c == ']') {
			if (buffer[index] != 0) {
				ri = openr[i];
				i = openi[i];
				continue;
			}
		}
		if (i < 0) {
			printf("ERROR i < 0: %d", i);
			break;
		}
		i++;
		if (i >= strlen(commands)) break;
	}
	return 0;
}

We launch the program for verification and enter:

7
1 2 3 -1 3 4 8
the result of executing a self-written C interpreter

the result of executing a self-written C interpreter

Excellent, the program outputted 4

What can be improved?

  • As far as I know, there are faster versions of interpreters

  • Most likely, the mathematical operations in brainfuck can be improved with the help of tricky mathematics, but I haven't delved into this area yet

  • Well, naturally it is worth writing more different functions like raising to a power and so on.

  • You can add variables that span multiple cells and do math with them.

  • Add different arrays, stacks and other useful things

Why did all this happen?

Well, why not, solving the problem this way was quite interesting.

Useful links

The most convenient development environment for Brainfuck and more: https://esolangpark.vercel.app/ide/brainfuck
Wiki on esoteric programming languages: https://esolangs.org/wiki
My generator: https://github.com/Agzam4/BrainfuckCodeGeneraor

P.S.:
waiting for doom on brainfuck

opened my repository from GitHub on mobile

opened my repository from GitHub on mobile

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *