aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharles Cabergs <me@cacharle.xyz>2021-11-21 13:51:27 +0100
committerCharles Cabergs <me@cacharle.xyz>2021-11-21 13:51:27 +0100
commit5677c3b8503292b02a516441ef9328b002142717 (patch)
tree1a8488235b9b8d8179662a3e5b583c69a71810b7
parenteeaf62eba40b81604c1208d4d74bdeebfccd6e15 (diff)
downloadbfc-5677c3b8503292b02a516441ef9328b002142717.tar.gz
bfc-5677c3b8503292b02a516441ef9328b002142717.tar.bz2
bfc-5677c3b8503292b02a516441ef9328b002142717.zip
Added assembler and linker in bfc.c instead of Makefile, Added options to choose output filename and if output is assembly or executable
-rw-r--r--.clang-format69
-rw-r--r--Makefile28
-rw-r--r--bfc.c247
3 files changed, 280 insertions, 64 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..f8821e3
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,69 @@
+---
+# BasedOnStyle: LLVM
+---
+
+Language: Cpp
+
+# PPIndentWidth: 1 # clang-format 13 only
+
+IndentWidth: 4
+TabWidth: 4
+UseTab: Never
+
+ColumnLimit: 100
+
+DerivePointerAlignment: false
+PointerAlignment: Right
+
+AlignConsecutiveDeclarations: Consecutive
+AlignConsecutiveAssignments: None
+AlignConsecutiveBitFields: Consecutive
+# AlignArrayOfStructures: Left # clang-format 13 only
+AlignEscapedNewlines: Left
+AlignTrailingComments: true
+AlignAfterOpenBracket: Align
+
+AlwaysBreakAfterDefinitionReturnType: All
+AlwaysBreakAfterReturnType: All
+
+SpaceBeforeParens: ControlStatements
+
+AllowShortFunctionsOnASingleLine: None
+AllowShortBlocksOnASingleLine: Never
+AllowShortIfStatementsOnASingleLine: Never
+
+AllowAllArgumentsOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+
+AlwaysBreakBeforeMultilineStrings: false
+
+BinPackArguments: false
+BinPackParameters: false
+
+BreakBeforeBraces: Allman
+BreakBeforeTernaryOperators: true
+
+IncludeBlocks: Merge
+
+KeepEmptyLinesAtTheStartOfBlocks: false
+MaxEmptyLinesToKeep: 1
+
+PenaltyBreakBeforeFirstCallParameter: 1
+PenaltyBreakString: 1
+PenaltyExcessCharacter: 1000
+
+SpaceAfterCStyleCast: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeSquareBrackets: false
+SpaceInEmptyParentheses: false
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesBeforeTrailingComments: 2
+SpacesInSquareBrackets: false
+SpaceAroundPointerQualifiers: Default
+
+AlignOperands: Align
+
+Cpp11BracedListStyle: true
+
+# vim:ft=yaml
diff --git a/Makefile b/Makefile
index 07a867d..8d1c0b8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,26 +1,13 @@
CC = gcc
CCFLAGS = -Wall -Wextra -std=c99
-NASM = nasm
-
-ifeq ($(shell uname),Linux)
- NASMFLAGS = -f elf64
-endif
-ifeq ($(shell uname),Darwin)
- NASMFLAGS = -f macho64
-endif
-ifeq ($(NASMFLAGS),)
- $(error architecture: $(shell uname) not recognized)
-endif
SRC = bfc.c
NAME = bfc
-ASMDIR = asm
BINDIR = bin
EXAMPLESDIR = examples
EXAMPLES = $(shell find $(EXAMPLESDIR) -name '*.bf' -type f)
-EXAMPLES_ASM = $(EXAMPLES:$(EXAMPLESDIR)/%.bf=$(ASMDIR)/%.asm)
EXAMPLES_NAME = $(EXAMPLES:$(EXAMPLESDIR)/%.bf=$(BINDIR)/%)
all: $(NAME) $(EXAMPLES_NAME)
@@ -28,26 +15,21 @@ all: $(NAME) $(EXAMPLES_NAME)
$(NAME): bfc.c
$(CC) $(CCFLAGS) -o $@ $<
-$(BINDIR)/%: $(ASMDIR)/%.asm $(BINDIR)
- $(NASM) $(NASMFLAGS) -o $@.o $<
- ld -o $@ $@.o
-
-$(ASMDIR)/%.asm: $(EXAMPLESDIR)/%.bf $(ASMDIR)
- ./$(NAME) < $< > $@
+$(BINDIR)/%: $(EXAMPLESDIR)/%.bf $(BINDIR)
+ ./$(NAME) -o $@ $<
$(BINDIR):
mkdir -pv $@
-$(ASMDIR):
- mkdir -pv $@
-
clean:
- - rm -rv $(ASMDIR)
- rm -rv $(BINDIR)
- rm $(NAME)
re: clean all
+format:
+ clang-format -i bfc.c
+
# weird GNU make behavior where it rm's every asm files at the end
# (https://stackoverflow.com/questions/47447369)
.PRECIOUS: $(EXAMPLES_ASM)
diff --git a/bfc.c b/bfc.c
index 30098d1..6fff9cd 100644
--- a/bfc.c
+++ b/bfc.c
@@ -1,52 +1,190 @@
+#define _XOPEN_SOURCE 500
+#include <ctype.h>
+#include <errno.h>
#include <getopt.h>
+#include <stdarg.h>
+#include <stdbool.h>
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
-static const size_t buffer_size = 256;
-/* static const char* asm_filename = "out.asm"; */
+#ifdef __linux__
+#define NASM_FORMAT "elf64"
+#endif
+#ifdef __APPLE__
+#define NASM_FORMAT "macho64"
+#endif
+#ifndef NASM_FORMAT
+#error "architecture not supported"
+#endif
-#define LABEL_COUNT_STACK_SIZE 256
+#define LABEL_COUNT_STACK_SIZE 4096
static size_t label_stack[LABEL_COUNT_STACK_SIZE + 1] = {0};
static size_t label_stack_frame = 0;
-int main(int argc, char *argv[])
+static bool compile_to_exec = true;
+static char *output_filename = NULL;
+static size_t buffer_len = 256;
+static size_t buffer_elem_bytes = 1;
+
+static char *elem_bytes_asm_str = NULL;
+static char *elem_bytes_asm_str_res = NULL;
+
+#define TMP_FILEPATH_TEMPLATE "/tmp/bfc_asm_XXXXXX"
+#define TMP_OBJ_FILEPATH_TEMPLATE (TMP_FILEPATH_TEMPLATE ".o")
+static char tmp_filepath[] = TMP_FILEPATH_TEMPLATE;
+static char tmp_obj_filepath[] = TMP_OBJ_FILEPATH_TEMPLATE;
+
+static void
+die(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fputs("bfc: ", stderr);
+ vfprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+static size_t
+xatoul(char *s)
+{
+ if (!isdigit(*s))
+ die("'%s' is not a valid number", s);
+ char *end;
+ size_t num = strtoul(s, &end, 10);
+ if (*end != '\0')
+ die("'%s' is not a valid number", s);
+ return num;
+}
+
+static void
+run_subprocess(char *name, char *const argv[])
{
- // int option;
-
- /* while ((option = getopt(argc, argv, "So:b:")) != -1) */
- /* { */
- /* switch (option) */
- /* { */
- /* case 'S': */
- /* assembly = true; */
- /* case 'o': */
- /* output_filename = optstring */
- /* case 'b': */
- /* buffer_length = atoi(optstring); */
- /* case 'e': */
- /* // byte, word, dword or qword */
- /* buffer_elem = x; */
- /* } */
- /* } */
+ pid_t pid = fork();
+ if (pid == -1)
+ die("%s", strerror(errno));
+ if (pid == 0)
+ {
+ execvp(name, argv);
+ die("%s", strerror(errno));
+ }
+ waitpid(pid, &pid, 0);
+ int status = WEXITSTATUS(pid);
+ if (status != 0)
+ die("%s process failed with %d\n", name, status);
+}
+
+static void
+remove_tmp_file(void)
+{
+ if (strcmp(tmp_filepath, TMP_FILEPATH_TEMPLATE) != 0)
+ remove(tmp_filepath);
+ if (strcmp(tmp_obj_filepath, TMP_OBJ_FILEPATH_TEMPLATE) != 0)
+ remove(tmp_obj_filepath);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int option;
+
+ while ((option = getopt(argc, argv, "So:b:e:h")) != -1)
+ {
+ switch (option)
+ {
+ case 'S':
+ compile_to_exec = false;
+ break;
+ case 'o':
+ output_filename = optarg;
+ break;
+ case 'b':
+ buffer_len = xatoul(optarg);
+ break;
+ case 'e':
+ buffer_elem_bytes = xatoul(optarg);
+ switch (buffer_elem_bytes)
+ {
+ case 1:
+ elem_bytes_asm_str = "byte";
+ elem_bytes_asm_str_res = "resb";
+ break;
+ case 2:
+ elem_bytes_asm_str = "word";
+ elem_bytes_asm_str_res = "resw";
+ break;
+ case 4:
+ elem_bytes_asm_str = "dword";
+ elem_bytes_asm_str_res = "resd";
+ break;
+ case 8:
+ elem_bytes_asm_str = "qword";
+ elem_bytes_asm_str_res = "resq";
+ break;
+ default:
+ die("%lu: is not a valid number of bytes for buffer element", buffer_elem_bytes);
+ }
+ break;
+ case 'h':
+ puts("Usage: bfc [-h] [-S] [-o file] [-b buffer_len] [-e "
+ "buffer_elem_bytes] [INPUT_FILE]");
+ puts("");
+ puts("INPUT_FILE Brainfuck source (read stdin if not present)");
+ puts("");
+ puts("-h Print this message");
+ puts("-S Output assembly instead of compiled executable");
+ puts("-o Output filename ('-' for stdout)");
+ puts("-b Length of the buffer available the program");
+ puts("-e Number of bytes in each element of the program's "
+ "buffer");
+ puts(" (can only be: 1, 2, 4 or 8)");
+ exit(EXIT_SUCCESS);
+ }
+ }
+ if (optind > argc + 1)
+ die("Unexpected argument: %s\n", argv[optind + 1]);
+
+ if (output_filename == NULL)
+ output_filename = compile_to_exec ? "a.out" : "a.asm";
+
+ atexit(remove_tmp_file);
+
FILE *input_file = NULL;
- if (argc == 2)
- input_file = fopen(argv[1], "r");
- else
+ if (optind == argc)
input_file = stdin;
+ else
+ {
+ input_file = fopen(argv[optind], "r");
+ if (input_file == NULL)
+ die("%s: %s\n", strerror(errno), argv[optind]);
+ }
+
+ FILE *output_file = NULL;
+ if (strcmp(output_filename, "-") == 0)
+ output_file = stdout;
+ else
+ {
+ if (compile_to_exec)
+ output_file = fdopen(mkstemp(tmp_filepath), "w");
+ else
+ output_file = fopen(output_filename, "w");
+ if (output_file == NULL)
+ die("%s: %s\n", strerror(errno), argv[optind]);
+ }
- /* FILE* output_file = fopen(asm_filename, "w"); */
- FILE *output_file = stdout;
- fprintf(
- output_file,
- "global _start\n\n"
- "section .bss\n"
- "\tbuffer: resb %zu\n\n"
- "section .text\n"
- "_start:\n"
- "\tmov rbx, buffer\n",
- buffer_size
- );
+ fprintf(output_file,
+ "global _start\n\n"
+ "section .bss\n"
+ "\tbuffer: resb %zu\n\n"
+ "section .text\n"
+ "_start:\n"
+ "\tmov rbx, buffer\n",
+ buffer_len);
size_t label_frame_id;
char c;
@@ -82,17 +220,29 @@ int main(int argc, char *argv[])
break;
case '[':
label_frame_id = label_stack[label_stack_frame];
- fprintf(output_file, "label_open_%03zu_%03zu: ; [\n", label_stack_frame, label_frame_id);
+ fprintf(output_file,
+ "label_open_%03zu_%03zu: ; [\n",
+ label_stack_frame,
+ label_frame_id);
fprintf(output_file, " cmp byte [rbx], 0 ; [\n");
- fprintf(output_file, " je label_close_%03zu_%03zu ; [\n", label_stack_frame, label_frame_id);
+ fprintf(output_file,
+ " je label_close_%03zu_%03zu ; [\n",
+ label_stack_frame,
+ label_frame_id);
label_stack_frame++;
break;
case ']':
label_stack_frame--;
label_frame_id = label_stack[label_stack_frame];
- fprintf(output_file, "label_close_%03zu_%03zu: ; ]\n", label_stack_frame, label_frame_id);
+ fprintf(output_file,
+ "label_close_%03zu_%03zu: ; ]\n",
+ label_stack_frame,
+ label_frame_id);
fprintf(output_file, " cmp byte [rbx], 0 ; ]\n");
- fprintf(output_file, " jne label_open_%03zu_%03zu ; ]\n", label_stack_frame, label_frame_id);
+ fprintf(output_file,
+ " jne label_open_%03zu_%03zu ; ]\n",
+ label_stack_frame,
+ label_frame_id);
label_stack[label_stack_frame]++;
break;
case ';':
@@ -103,10 +253,25 @@ int main(int argc, char *argv[])
break;
}
}
+ if (input_file != stdin)
+ fclose(input_file);
fprintf(output_file, "\n");
fprintf(output_file, " mov rdi, 0 ; exit\n");
fprintf(output_file, " mov rax, 60 ; exit\n");
fprintf(output_file, " syscall ; exit\n");
+ if (output_file != stdout)
+ fclose(output_file);
+
+ if (compile_to_exec)
+ {
+ strcpy(tmp_obj_filepath, tmp_filepath);
+ strcat(tmp_obj_filepath, ".o");
+ char *const nasm_argv[] = {
+ "nasm", "-f", NASM_FORMAT, "-o", tmp_obj_filepath, tmp_filepath, NULL};
+ run_subprocess("nasm", nasm_argv);
+ char *const ld_argv[] = {"ld", "-o", output_filename, tmp_obj_filepath, NULL};
+ run_subprocess("ld", ld_argv);
+ }
return 0;
}