Files
complife/src/bf.c

173 lines
3.9 KiB
C

/* bf.c: Program iteration implementation
* Created: 2026-03-10
* Author: Aryadev Chavali
* License: See end of file
*/
#include <string.h>
#include "bf.h"
#include "vec.h"
void program_concat(struct ProgramConcat *ret, bf_token *a, bf_token *b)
{
assert(a && b);
ret->a = a;
ret->b = b;
memcpy(ret->tape, a, SIZEOF_PROGRAM);
memcpy(ret->tape + SIZEOF_PROGRAM, b, SIZEOF_PROGRAM);
}
// Represents the current execution context of the BrainFuck VM implementation.
// Used internally only, so its defined here.
struct MachineContext
{
u64 ip, head0, head1;
u64 total_iters;
};
// Jump-forward operator of BrainFuck `[`. We maintain a `loop_stack` which
// allows us to trivially implement the jump-back operator `]` - essentially
// heavy forward investment.
void loop_begin(struct ProgramConcat *prg, struct MachineContext *ctx,
vec_t *loop_stack)
{
// Add to loop_stack if not already present.
if (vec_find(loop_stack, &ctx->ip, sizeof(ctx->ip)) > loop_stack->size)
{
vec_append(loop_stack, &ctx->ip, sizeof(ctx->ip));
}
// If tape at head0 is nonzero, then go to the next instruction.
if (prg->tape[ctx->head0])
{
++ctx->ip;
return;
}
// Otherwise, we need to find the corresponding closed bracket and jump to it.
u64 square_brackets = 1;
u64 close_ip;
for (close_ip = ctx->ip + 1;
square_brackets != 0 && close_ip < sizeof(prg->tape); ++close_ip)
{
switch (prg->tape[close_ip])
{
case '[':
++square_brackets;
break;
case ']':
--square_brackets;
break;
}
}
if (!square_brackets)
{
ctx->ip = close_ip;
}
else
{
// NOTE: as per paper, terminate.
ctx->ip = sizeof(prg->tape);
}
}
void loop_end(struct ProgramConcat *prg, struct MachineContext *ctx,
vec_t *loop_stack)
{
if (!prg->tape[ctx->head0])
{
++ctx->ip;
}
else if (loop_stack->size < sizeof(u64))
{
// NOTE: as per paper, terminate.
ctx->ip = sizeof(prg->tape);
}
else
{
ctx->ip = *(u64 *)vec_pop(loop_stack, sizeof(ctx->ip));
}
}
static vec_t loop_stack = {0};
void program_execute(struct ProgramConcat *prg)
{
loop_stack.size = 0;
vec_ensure_capacity(&loop_stack, sizeof(prg->tape) * sizeof(u64));
for (struct MachineContext ctx = {0};
ctx.ip < sizeof(prg->tape) && ctx.total_iters < (1LU << 13);
++ctx.total_iters)
{
u8 opcode = prg->tape[ctx.ip];
switch (opcode)
{
case '<':
ctx.head0 -= 1;
ctx.head0 %= sizeof(prg->tape);
++ctx.ip;
break;
case '>':
ctx.head0++;
++ctx.ip;
break;
case '{':
ctx.head1 -= 1;
ctx.head1 %= sizeof(prg->tape);
++ctx.ip;
break;
case '}':
ctx.head1++;
++ctx.ip;
break;
case '-':
prg->tape[ctx.head0]--;
++ctx.ip;
break;
case '+':
prg->tape[ctx.head0]++;
++ctx.ip;
break;
case '.':
prg->tape[ctx.head1] = prg->tape[ctx.head0];
++ctx.ip;
break;
case ',':
prg->tape[ctx.head0] = prg->tape[ctx.head1];
++ctx.ip;
break;
case '[':
loop_begin(prg, &ctx, &loop_stack);
break;
case ']':
loop_end(prg, &ctx, &loop_stack);
break;
default:
++ctx.ip;
break;
}
}
}
void program_split(struct ProgramConcat *prg)
{
assert(prg->a && prg->b);
memcpy(prg->a, prg->tape, SIZEOF_PROGRAM);
memcpy(prg->b, prg->tape + SIZEOF_PROGRAM, SIZEOF_PROGRAM);
}
/* Copyright (C) 2026 Aryadev Chavali
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
* details.
* You may distribute and modify this code under the terms of the GNU General
* Public License Version 2, which you should have received a copy of along with
* this program. If not, please go to <https://www.gnu.org/licenses/>.
*/