QUOTE: Let your heart guide you always.

beep

A tiny terminal-based ASCII circuit simulator

commit 458aa029a8b9dce801f9818497560e95cab17bb3
Author: Sophie <info@soophie.de>
Date:   Fri, 16 May 2025 01:10:36 +0200

feat: Initial commit

Diffstat:
A.gitignore | 1+
AMakefile | 19+++++++++++++++++++
Afiles/bus | 5+++++
Afiles/not | 3+++
Afiles/sample | 5+++++
Asrc/main.c | 583+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 616 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1 @@ +beep diff --git a/Makefile b/Makefile @@ -0,0 +1,19 @@ +TARGET=beep +SOURCE=src/*.c + +CC=cc +CFLAGS=-Wall -Wextra -Werror -pedantic +LIBS= + +.PHONY: all build run clean + +all: build + +build: + $(CC) $(CFLAGS) -o ./$(TARGET) $(SOURCE) $(LIBS) + +run: build + ./$(TARGET) + +clean: + rm -f ./$(TARGET) diff --git a/files/bus b/files/bus @@ -0,0 +1,5 @@ +REG($1)--->[x]---+ + | + +---ADD---REG($3) + | +REG($2)--->[y]---+---<[x]---REG($4) diff --git a/files/not b/files/not @@ -0,0 +1,3 @@ ++---NOT---+ +| | ++---------+ diff --git a/files/sample b/files/sample @@ -0,0 +1,5 @@ +BTN---+ + | + +---NOT---BULB + | +BTN---+ diff --git a/src/main.c b/src/main.c @@ -0,0 +1,583 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <wctype.h> + +#define BEEP_VALUE_HIGH 1 +#define BEEP_VALUE_LOW 0 + +typedef enum { + BEEP_OK = 0, + BEEP_ERROR = -1, +} beep_err_t; + +typedef long beep_uid_t; + +typedef struct beep_node beep_node_t; +typedef struct beep_element beep_element_t; + +typedef enum { + BEEP_TYPE_NONE, + BEEP_TYPE_WIRE_Y, + BEEP_TYPE_WIRE_X, + BEEP_TYPE_NODE, + BEEP_TYPE_ELEMENT, +} beep_type_t; + +typedef struct { + beep_type_t type; + char *str; + size_t len; + size_t ref_idx; +} beep_token_t; + +typedef struct { + beep_token_t *token; +} beep_cell_t; + +typedef int beep_value_t; + +typedef struct { + beep_uid_t element_uid; + size_t idx; + beep_value_t value; +} beep_channel_t; + +struct beep_node { + beep_uid_t uid; + beep_node_t **nodes; + size_t nodes_len; + beep_channel_t *channels; + size_t channels_len; +}; + +struct beep_element { + beep_uid_t uid; + beep_node_t *in; + beep_node_t *out; + void (*update)(beep_element_t *self); +}; + +typedef struct { + beep_uid_t element_uid; + beep_value_t value; +} beep_signal_t; + +typedef struct { + beep_element_t *elements; + size_t elements_len; + beep_node_t *nodes; + size_t nodes_len; +} beep_state_t; + +static beep_uid_t _BEEP_UID_COUNT = 0; +static beep_value_t _BEEP_BTN = BEEP_VALUE_LOW; + +beep_uid_t beep_uid(void) { + return ++_BEEP_UID_COUNT; +} + +void beep_node_init(beep_node_t *self) { + *self = (beep_node_t) { + .uid = beep_uid(), + .nodes = NULL, + .nodes_len = 0, + .channels = NULL, + .channels_len = 0, + }; +} + +void beep_node_index(beep_node_t *self, beep_uid_t element_uid, size_t idx) { + size_t i = 0; + for (; i < self->channels_len; i++) { + if (self->channels[i].element_uid == element_uid) { + return; + } + } + self->channels = realloc(self->channels, sizeof(beep_channel_t) * (self->channels_len + 1)); + beep_channel_t *channel = &self->channels[self->channels_len]; + channel->element_uid = element_uid; + channel->idx = idx; + channel->value = 0; + self->channels_len++; + i = 0; + for (; i < self->nodes_len; i++) { + beep_node_t *node = self->nodes[i]; + beep_node_index(node, element_uid, idx + 1); + } +} + +void beep_node_update(beep_node_t *self, const beep_signal_t signal) { + size_t i = 0; + beep_channel_t *channel = NULL; + for (; i < self->channels_len; i++) { + if (self->channels[i].element_uid == signal.element_uid) { + channel = &self->channels[i]; + break; + } + } + if (channel == NULL) { + return; + } + channel->value = signal.value; + i = 0; + for (; i < self->nodes_len; i++) { + beep_node_t *node = self->nodes[i]; + size_t j = 0; + for (; j < node->channels_len; j++) { + beep_channel_t *node_channel = &node->channels[j]; + if (node_channel->element_uid == channel->element_uid && node_channel->idx > channel->idx) { + beep_node_update(node, signal); + break; + } + } + } +} + +beep_value_t beep_node_value(beep_node_t *self) { + size_t i = 0; + for (; i < self->channels_len; i++) { + beep_channel_t *channel = &self->channels[i]; + if (channel->value == BEEP_VALUE_HIGH) { + return BEEP_VALUE_HIGH; + } + } + return BEEP_VALUE_LOW; +} + +void beep_element_init(beep_element_t *self, void (*update)(beep_element_t *self)) { + beep_node_t *node_in = malloc(sizeof(beep_node_t)); + beep_node_init(node_in); + beep_node_t *node_out = malloc(sizeof(beep_node_t)); + beep_node_init(node_out); + *self = (beep_element_t) { + .uid = beep_uid(), + .in = node_in, + .out = node_out, + .update = update, + }; +} + +typedef struct { + char *buffer; + size_t rows; + size_t cols; + beep_cell_t *cells; + beep_token_t *tokens; + size_t len; + beep_state_t state; +} beep_board_t; + +beep_cell_t *beep_board_at(beep_board_t *self, size_t row, size_t col) { + if (self == NULL) { + return NULL; + } + if (row >= self->rows || col >= self->cols) { + return NULL; + } + return &self->cells[row * self->cols + col]; +} + +beep_err_t beep_file_read(char *filename, char **buffer) { + FILE *file = fopen(filename, "r"); + if (file == NULL) { + return BEEP_ERROR; + } + fseek(file, 0, SEEK_END); + size_t len = ftell(file); + fseek(file, 0, SEEK_SET); + *buffer = malloc(sizeof(char) * (len + 1)); + fread(*buffer, sizeof(char), len, file); + (*buffer)[len] = '\0'; + fclose(file); + return BEEP_OK; +} + +beep_err_t beep_board_read(beep_board_t *self, char *buffer) { + if (self == NULL) { + return BEEP_ERROR; + } + // read dimensions (rows, cols) + size_t len = strlen(buffer); + size_t i = 0; + size_t ln_set = i; + for (; i < len; i++) { + char c = buffer[i]; + if (c == '\n') { + size_t ln_len = i - ln_set; + if (i > 0 && buffer[i - 1] == '\r') { + ln_len--; + } + if (ln_len > self->cols) { + self->cols = ln_len; + } + ln_set = i + 1; + self->rows++; + } + } + if (ln_set < len) { + size_t ln_len = i - ln_set; + if (i > 0 && buffer[i - 1] == '\r') { + ln_len--; + } + if (ln_len > self->cols) { + self->cols = ln_len; + } + self->rows++; + } + // fill board + self->buffer = malloc(sizeof(char) * self->rows * self->cols); + memset(self->buffer, ' ', self->rows * self->cols); + i = 0; + ln_set = i; + size_t ln = i; + for (; i < len; i++) { + size_t ln_idx = i - ln_set; + char c = buffer[i]; + if (c == '\r') { + continue; + } + if (c == '\n') { + ln_set = i + 1; + ln++; + continue; + } + if (ln_idx < self->cols) { + self->buffer[ln * self->cols + ln_idx] = c; + } + } + return BEEP_OK; +} + +beep_err_t beep_board_tokenize(beep_board_t *self) { + if (self == NULL) { + return BEEP_ERROR; + } + // read tokens + size_t i = 0; + size_t len = self->rows * self->cols; + for (; i < len; i++) { + char c = self->buffer[i]; + if (c == ' ' || c == '-' || c == '|' || c == '+') { + self->tokens = realloc(self->tokens, sizeof(beep_token_t) * (self->len + 1)); + beep_token_t *token = &self->tokens[self->len]; + switch (c) { + case ' ': + token->type = BEEP_TYPE_NONE; + break; + case '-': + token->type = BEEP_TYPE_WIRE_X; + break; + case '|': + token->type = BEEP_TYPE_WIRE_Y; + break; + case '+': + token->type = BEEP_TYPE_NODE; + break; + } + token->str = self->buffer + i; + token->len = 1; + self->len++; + } + if (isalpha(c)) { + size_t token_set = i; + while (i < len && isalpha(self->buffer[i])) { + i++; + } + self->tokens = realloc(self->tokens, sizeof(beep_token_t) * (self->len + 1)); + beep_token_t *token = &self->tokens[self->len]; + token->type = BEEP_TYPE_ELEMENT; + token->str = self->buffer + token_set; + token->len = i - token_set; + self->len++; + i--; + } + } + // create cells + self->cells = calloc(self->rows * self->cols, sizeof(beep_cell_t)); + i = 0; + size_t cell_idx = i; + for (; i < self->len; i++) { + beep_token_t *token = &self->tokens[i]; + size_t j = 0; + for (; j < token->len; j++) { + beep_cell_t *cell = &self->cells[cell_idx + j]; + cell->token = token; + } + cell_idx += token->len; + } + return BEEP_OK; +} + +void BEEP_BTN_UPDATE(beep_element_t *self) { + beep_value_t value_out = _BEEP_BTN; + printf("[BTN] %s\n", value_out ? "HIGH" : "LOW"); + beep_node_update(self->out, (beep_signal_t) { self->uid, value_out }); +} + +void BEEP_NOT_UPDATE(beep_element_t *self) { + beep_value_t value_in = beep_node_value(self->in); + beep_value_t value_out = !value_in; + printf("[NOT] %s --> %s\n", value_in ? "HIGH" : "LOW", value_out ? "HIGH" : "LOW"); + beep_node_update(self->out, (beep_signal_t) { self->uid, value_out }); +} + +void BEEP_BULB_UPDATE(beep_element_t *self) { + beep_value_t value_in = beep_node_value(self->in); + printf("[BULB] %s\n", value_in ? "HIGH" : "LOW"); +} + +beep_err_t beep_board_construct(beep_board_t *self) { + if (self == NULL) { + return BEEP_ERROR; + } + beep_state_t *state = &self->state; + // create elements/nodes + size_t i = 0; + for (; i < self->len; i++) { + beep_token_t *token = &self->tokens[i]; + if (token->type == BEEP_TYPE_ELEMENT) { + state->elements = realloc(state->elements, sizeof(beep_element_t) * (state->elements_len + 1)); + beep_element_t *element = &state->elements[state->elements_len]; + char element_type[token->len + 1]; + memcpy(element_type, token->str, token->len); + element_type[token->len] = '\0'; + void (*update)(beep_element_t *self) = NULL; + if (strcmp(element_type, "BTN") == 0) { + update = BEEP_BTN_UPDATE; + } + if (strcmp(element_type, "NOT") == 0) { + update = BEEP_NOT_UPDATE; + } + if (strcmp(element_type, "BULB") == 0) { + update = BEEP_BULB_UPDATE; + } + beep_element_init(element, update); + token->ref_idx = state->elements_len; + state->elements_len++; + } + if (token->type == BEEP_TYPE_NODE) { + state->nodes = realloc(state->nodes, sizeof(beep_node_t) * (state->nodes_len + 1)); + beep_node_t *node = &state->nodes[state->nodes_len]; + beep_node_init(node); + token->ref_idx = state->nodes_len; + state->nodes_len++; + } + } + // link all horizontal wires + for (size_t y = 0; y < self->rows; y++) { + size_t x = 0; + beep_cell_t *cell_set = NULL; + while (x < self->cols) { + beep_cell_t *cell = beep_board_at(self, y, x); + if (cell->token->type == BEEP_TYPE_WIRE_X) { + if (x > 0) { + cell_set = beep_board_at(self, y, x - 1); + } + while (x < self->cols && cell->token->type == BEEP_TYPE_WIRE_X) { + cell = beep_board_at(self, y, ++x); + } + if (cell != NULL && cell_set != NULL) { + beep_type_t type_set = cell_set->token->type; + beep_type_t type_end = cell->token->type; + if ( + (type_set == BEEP_TYPE_ELEMENT || type_set == BEEP_TYPE_NODE) && + (type_end == BEEP_TYPE_ELEMENT || type_end == BEEP_TYPE_NODE) + ) { + beep_node_t *node_set = NULL; + beep_node_t *node_end = NULL; + if (type_set == BEEP_TYPE_ELEMENT) { + beep_element_t *element = &state->elements[cell_set->token->ref_idx]; + node_set = element->out; + } + if (type_set == BEEP_TYPE_NODE) { + node_set = &state->nodes[cell_set->token->ref_idx]; + } + if (type_end == BEEP_TYPE_ELEMENT) { + beep_element_t *element = &state->elements[cell->token->ref_idx]; + node_end = element->in; + } + if (type_end == BEEP_TYPE_NODE) { + node_end = &state->nodes[cell->token->ref_idx]; + } + if (node_set != NULL && node_end != NULL) { + node_set->nodes = realloc(node_set->nodes, sizeof(beep_node_t *) * (node_set->nodes_len + 1)); + node_set->nodes[node_set->nodes_len] = node_end; + node_set->nodes_len++; + node_end->nodes = realloc(node_end->nodes, sizeof(beep_node_t *) * (node_end->nodes_len + 1)); + node_end->nodes[node_end->nodes_len] = node_set; + node_end->nodes_len++; + } + } + } + x--; + } + x++; + } + } + // link all vertical wires + for (size_t x = 0; x < self->cols; x++) { + size_t y = 0; + beep_cell_t *cell_set = NULL; + while (y < self->rows) { + beep_cell_t *cell = beep_board_at(self, y, x); + if (cell->token->type == BEEP_TYPE_WIRE_Y) { + if (y > 0) { + cell_set = beep_board_at(self, y - 1, x); + } + while (y < self->rows && cell->token->type == BEEP_TYPE_WIRE_Y) { + cell = beep_board_at(self, ++y, x); + } + if (cell != NULL && cell_set != NULL) { + beep_type_t type_set = cell_set->token->type; + beep_type_t type_end = cell->token->type; + if (type_set == BEEP_TYPE_NODE && type_end == BEEP_TYPE_NODE) { + beep_node_t *node_set = &state->nodes[cell_set->token->ref_idx]; + beep_node_t *node_end = &state->nodes[cell->token->ref_idx]; + node_set->nodes = realloc(node_set->nodes, sizeof(beep_node_t *) * (node_set->nodes_len + 1)); + node_set->nodes[node_set->nodes_len] = node_end; + node_set->nodes_len++; + node_end->nodes = realloc(node_end->nodes, sizeof(beep_node_t *) * (node_end->nodes_len + 1)); + node_end->nodes[node_end->nodes_len] = node_set; + node_end->nodes_len++; + } + } + y--; + } + y++; + } + } + // channel indexing + i = 0; + for (; i < state->elements_len; i++) { + beep_element_t *element = &state->elements[i]; + beep_node_index(element->out, element->uid, 0); + } + return BEEP_OK; +} + +beep_err_t beep_board_init(beep_board_t *self, char *buffer) { + if (buffer == NULL) { + return BEEP_ERROR; + } + *self = (beep_board_t) { + .buffer = NULL, + .rows = 0, + .cols = 0, + .cells = NULL, + .tokens = NULL, + .len = 0, + .state = {0}, + }; + beep_err_t ret; + if ((ret = beep_board_read(self, buffer)) == BEEP_ERROR) { + return ret; + } + if ((ret = beep_board_tokenize(self)) == BEEP_ERROR) { + return ret; + } + if ((ret = beep_board_construct(self)) == BEEP_ERROR) { + return ret; + } + return BEEP_OK; +} + +beep_err_t beep_board_load(beep_board_t *self, char *filename) { + char *buffer = NULL; + if (beep_file_read(filename, &buffer) < 0) { + return BEEP_ERROR; + } + beep_err_t ret = beep_board_init(self, buffer); + free(buffer); + return ret; +} + +void beep_board_free(beep_board_t *self) { + if (self == NULL) { + return; + } + free(self->buffer); + free(self->cells); + free(self->tokens); + beep_state_t *state = &self->state; + if (state->elements_len > 0) { + size_t i = 0; + for (; i < state->elements_len; i++) { + beep_element_t *element = &state->elements[i]; + free(element->in); + free(element->out); + } + free(state->elements); + } + if (state->nodes_len > 0) { + size_t i = 0; + for (; i < state->nodes_len; i++) { + beep_node_t *node = &state->nodes[i]; + if (node->nodes_len > 0) { + free(node->nodes); + } + if (node->channels_len > 0) { + free(node->channels); + } + } + free(state->nodes); + } +} + +void beep_update(beep_board_t *self) { + printf("----- update -----\n"); + beep_state_t *state = &self->state; + size_t i = 0; + for (; i < state->elements_len; i++) { + beep_element_t *element = &state->elements[i]; + element->update(element); + } +} + +int main(void) { + beep_board_t board; + if (beep_board_load(&board, "./files/not") < 0) { + return BEEP_ERROR; + } + for (size_t y = 0; y < board.rows; y++) { + for (size_t x = 0; x < board.cols; x++) { + printf("%c", board.buffer[y * board.cols + x]); + } + printf("\n"); + } + printf("\n"); + for (size_t y = 0; y < board.rows; y++) { + for (size_t x = 0; x < board.cols; x++) { + size_t idx = y * board.cols + x; + beep_cell_t *cell = &board.cells[idx]; + switch (cell->token->type) { + case BEEP_TYPE_NONE: + printf("."); + break; + case BEEP_TYPE_WIRE_Y: + printf("Y"); + break; + case BEEP_TYPE_WIRE_X: + printf("X"); + break; + case BEEP_TYPE_NODE: + printf("N"); + break; + case BEEP_TYPE_ELEMENT: + printf("E"); + break; + } + } + printf("\n"); + } + printf("\n"); + beep_update(&board); + _BEEP_BTN = BEEP_VALUE_HIGH; + beep_update(&board); + _BEEP_BTN = BEEP_VALUE_LOW; + beep_update(&board); + beep_update(&board); + beep_board_free(&board); + return BEEP_OK; +}