-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 97c4248
Showing
9 changed files
with
457 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
|
||
.vscode/ | ||
build/ | ||
*.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
cmake_minimum_required(VERSION 2.6) | ||
project(omabfc C) | ||
|
||
set(CMAKE_C_STANDARD 99) | ||
|
||
file(GLOB_RECURSE SOURCES src/*.c) | ||
include_directories(src) | ||
|
||
add_executable(omabfc ${SOURCES}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
Copyright (c) 2019 Paweł Cholewa | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# Oh my... Another Brainfuck Compiler - omabfc | ||
OMABFC is a Brainfuck compiler written in C that produces NASM-compatible | ||
x64 Linux assembly code. | ||
|
||
## Usage | ||
OMABFC is a command-line tool. Available options: | ||
|
||
* **--input-file | -i**: provides input Brainfuck file (required), | ||
* **--output-file | -o**: provides output file for assembly code (by default code is outputted to stdout), | ||
* **--version | -v**: prints OMABFC version to stdout. | ||
|
||
## Actually building Brainfuck programs into executables | ||
OMABFC only produces assembly code. This means, that it needs | ||
to be manually assembled and linked. The process is fairly simple. | ||
|
||
1. Compile a Brainfuck file: `omabfc -i program.bf -o program.asm` | ||
2. Assemble it using nasm: `nasm -f elf64 program.asm -o program.o` | ||
3. Link it: `ld program.o -o program` | ||
|
||
And that's it. | ||
|
||
## Building OMABFC | ||
You need to get CMake and GCC. Go into root directory of the repostiory with | ||
a terminal, make a directory called `build`, go to it and execute `cmake ..` from it. | ||
Now run `make all`. After the compilation succeeds, `omabfc` file should appear | ||
in the `build` directory. | ||
|
||
## License | ||
This project is licensed under the [MIT License](LICENSE). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
#include "codegenx64.h" | ||
#include <stdlib.h> | ||
|
||
#define MEMSET_REGISTER "rax" /* Register used to 0 the tape */ | ||
#define TAPE_SIZE "10000" /* size of the tape (as string to directly embed the value in result code) */ | ||
#define TAPE_POINTER_REGISTER "rsi" /* pointer used to hold current tape element address */ | ||
|
||
static int loop_counter; | ||
static void generate_symbol(struct symbol_data* data, FILE* output); | ||
|
||
static void generate_vinc(FILE* output, unsigned int amount) | ||
{ | ||
if (amount == 0) | ||
return; | ||
|
||
if (amount == 1) | ||
fprintf(output, " inc byte [" TAPE_POINTER_REGISTER "]\n"); | ||
else | ||
fprintf(output, " add " TAPE_POINTER_REGISTER ", %u\n", amount); | ||
} | ||
|
||
static void generate_vdec(FILE* output, unsigned int amount) | ||
{ | ||
if (amount == 0) | ||
return; | ||
|
||
if (amount == 1) | ||
fprintf(output, " dec byte [" TAPE_POINTER_REGISTER "]\n"); | ||
else | ||
fprintf(output, " sub byte [" TAPE_POINTER_REGISTER "], %u\n", amount); | ||
} | ||
|
||
static void generate_pinc(FILE* output, unsigned int amount) | ||
{ | ||
if (amount == 0) | ||
return; | ||
|
||
if (amount == 1) | ||
fprintf(output, " inc " TAPE_POINTER_REGISTER "\n"); | ||
else | ||
fprintf(output, " add " TAPE_POINTER_REGISTER ", %u\n", amount); | ||
} | ||
|
||
static void generate_pdec(FILE* output, unsigned int amount) | ||
{ | ||
if (amount == 0) | ||
return; | ||
|
||
if (amount == 1) | ||
fprintf(output, " dec " TAPE_POINTER_REGISTER "\n"); | ||
else | ||
fprintf(output, " sub " TAPE_POINTER_REGISTER ", %u\n", amount); | ||
} | ||
|
||
static void generate_out(FILE* output, unsigned int amount) | ||
{ | ||
if (amount == 0) | ||
return; | ||
|
||
fprintf(output, " mov rax, 1\n"); | ||
fprintf(output, " mov rdi, 1\n"); | ||
fprintf(output, " mov rdx, 1\n"); | ||
for (int i = 0; i < amount; i++) | ||
{ | ||
fprintf(output, " syscall\n\n"); | ||
} | ||
} | ||
|
||
static void generate_in(FILE* output, unsigned int amount) | ||
{ | ||
if (amount == 0) | ||
return; | ||
|
||
fprintf(output, " mov rax, 0\n"); | ||
fprintf(output, " mov rdi, 0\n"); | ||
fprintf(output, " mov rdx, 1\n"); | ||
for (int i = 0; i < amount; i++) | ||
{ | ||
fprintf(output, " syscall\n\n"); | ||
} | ||
} | ||
|
||
static void generate_loop(FILE* output, struct symbol_data* loop_symbol) | ||
{ | ||
int used_loop = loop_counter++; | ||
fprintf(output, "loop%u:\n", used_loop); | ||
fprintf(output, " cmp byte [" TAPE_POINTER_REGISTER "], 0\n"); | ||
fprintf(output, " je loopend%u\n", used_loop); | ||
|
||
struct symbol_data* current = loop_symbol->loop_data.loop_start; | ||
while (current) | ||
{ | ||
generate_symbol(current, output); | ||
current = current->next; | ||
} | ||
|
||
fprintf(output, " cmp byte [" TAPE_POINTER_REGISTER "], 0\n"); | ||
fprintf(output, " jne loop%u\n", used_loop); | ||
fprintf(output, "loopend%u:\n", used_loop); | ||
} | ||
|
||
static void generate_entry(FILE* output) | ||
{ | ||
fprintf(output, "; Generated by OMABFC\n\n"); | ||
fprintf(output, "section .bss\n"); | ||
fprintf(output, " tape resb " TAPE_SIZE "\n"); | ||
fprintf(output, "section .text\n"); | ||
fprintf(output, "global _start\n"); | ||
fprintf(output, "_start:\n"); | ||
fprintf(output, " mov " TAPE_POINTER_REGISTER ", tape\n"); | ||
fprintf(output, " mov " MEMSET_REGISTER ", " TAPE_SIZE "\n"); | ||
fprintf(output, "memset_loop:\n"); | ||
fprintf(output, " cmp " MEMSET_REGISTER ", 0\n"); | ||
fprintf(output, " je program\n"); | ||
fprintf(output, " mov byte [tape + " MEMSET_REGISTER "], 0\n"); | ||
fprintf(output, " dec " MEMSET_REGISTER "\n"); | ||
fprintf(output, " jmp memset_loop\n"); | ||
fprintf(output, "program:\n"); | ||
} | ||
|
||
static void generate_exit(FILE* output) | ||
{ | ||
fprintf(output, " mov rax, 60\n"); | ||
fprintf(output, " mov rdi, 0\n"); | ||
fprintf(output, " syscall\n\n"); | ||
} | ||
|
||
static void generate_symbol(struct symbol_data* data, FILE* output) | ||
{ | ||
switch (data->type) | ||
{ | ||
case SYMBOL_IN: | ||
generate_in(output, data->amount); | ||
break; | ||
case SYMBOL_OUT: | ||
generate_out(output, data->amount); | ||
break; | ||
case SYMBOL_VINC: | ||
generate_vinc(output, data->amount); | ||
break; | ||
case SYMBOL_VDEC: | ||
generate_vdec(output, data->amount); | ||
break; | ||
case SYMBOL_PINC: | ||
generate_pinc(output, data->amount); | ||
break; | ||
case SYMBOL_PDEC: | ||
generate_pdec(output, data->amount); | ||
break; | ||
case SYMBOL_LSTART: | ||
generate_loop(output, data); | ||
break; | ||
default: | ||
fprintf(stderr, "ERROR: Unknown symbol %d (%c)\n", data->type, data->type); | ||
exit(1); | ||
break; | ||
} | ||
} | ||
|
||
void generate_code(struct symbol_data* data, FILE* output) | ||
{ | ||
loop_counter = 0; | ||
|
||
/* Generate the initial code template */ | ||
generate_entry(output); | ||
|
||
while (data) | ||
{ | ||
generate_symbol(data, output); | ||
data = data->next; | ||
} | ||
|
||
/* And the exit syscall... */ | ||
generate_exit(output); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#ifndef __CODEGEN_H | ||
#define __CODEGEN_H | ||
|
||
#include <stdio.h> | ||
#include "symtab.h" | ||
|
||
void generate_code(struct symbol_data* data, FILE* output); | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
#include <stdio.h> | ||
#include <getopt.h> | ||
#include <stdarg.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include "symtab.h" | ||
#include "codegenx64.h" | ||
|
||
#define OMABFC_VERSION 10000 | ||
|
||
static struct option long_options[] = | ||
{ | ||
{ "version", no_argument, NULL, 'v' }, | ||
{ "input-file", required_argument, NULL, 'i' }, | ||
{ "output-file", required_argument, NULL, 'o' } | ||
}; | ||
|
||
static char* output_file_name = NULL; | ||
|
||
static FILE* input_file; | ||
static FILE* output_file; | ||
|
||
static void cleanup() | ||
{ | ||
if (input_file != NULL && input_file != stdin) | ||
fclose(input_file); | ||
if (output_file != NULL && output_file != stdout) | ||
fclose(output_file); | ||
} | ||
|
||
static void error(const char* fmt, ...) | ||
{ | ||
va_list list; | ||
va_start(list, fmt); | ||
vfprintf(stderr, fmt, list); | ||
va_end(list); | ||
cleanup(); | ||
exit(1); | ||
} | ||
|
||
int main(int argc, char* argv[]) | ||
{ | ||
output_file = stdout; | ||
|
||
int option; | ||
while ((option = getopt_long(argc, argv, "vi:o:", long_options, NULL)) != -1) | ||
{ | ||
switch (option) | ||
{ | ||
case 'v': | ||
printf("omabfc version %06d\n", (int) OMABFC_VERSION); | ||
cleanup(); | ||
exit(0); | ||
case 'o': | ||
output_file_name = malloc(strlen(optarg) + 1); | ||
memset(output_file_name, 0, strlen(optarg) + 1); | ||
strcpy(output_file_name, optarg); | ||
break; | ||
case 'i': | ||
input_file = fopen(optarg, "r"); | ||
if (!input_file) | ||
error("Couldn't open the input file!\n"); | ||
break; | ||
} | ||
} | ||
|
||
if (!input_file) | ||
error("Input file not specified\n"); | ||
|
||
if (output_file_name) | ||
{ | ||
output_file = fopen(output_file_name, "w"); | ||
if (!output_file) | ||
error("Couldn't open the output file!\n"); | ||
} | ||
|
||
fseek(input_file, 0, SEEK_END); | ||
size_t input_size = ftell(input_file); | ||
fseek(input_file, 0, SEEK_SET); | ||
char* input = malloc(input_size + 1); | ||
input[input_size] = '\0'; | ||
|
||
if (fread(input, sizeof(char), input_size, input_file) != input_size) | ||
error("Couldn't read from the input file!\n"); | ||
|
||
generate_code(parse(input), output_file); | ||
free(input); | ||
return 0; | ||
} |
Oops, something went wrong.