-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbf2c.c
141 lines (128 loc) · 4.78 KB
/
bf2c.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*
* bf2c.c
*
* Copyright 2017 Nigel Nquande <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the MIT license.
*/
#include <stdio.h>
#include <stdlib.h>
// #include <sys/unistd.h>
#include <string.h>
#define DEBUG 0 /* Change this to 1 to see debug output */
/* number of characters to reserve per instruction.
* was 10; increased as the reserved array was too short and caused a segmentation fault.
* Increase this if you encounter segfaults.
*/
#define TRANSLATION_EXPANDER 20
size_t loopIndent = 0;
/* Copy chars from src to dest, returning the number of chars copied */
static unsigned int str_copy(const char *src, char *dst) {
unsigned int i = 0;
while (src[i] != '\0') {
dst[i] = src[i];
i++;
}
return i;
}
/* Translate a BF character instruction (token) to the C equivalent */
static const char * codeGen(char token) {
switch (token) {
case '+': return "\to[i]++;\n";
case '-': return "\to[i]--;\n";
case '>': return "\ti++;\n";
case '<': return "\ti--;\n";
case '.': return "\tputchar (o[i]);\n";
case ',': return "\to[i] = getchar();\n";
case '[': ++loopIndent; return "\twhile (o[i] > 0) {\n";
case ']': --loopIndent; return "}\n\n";
default: return "";
}
}
/* Given BF input, print C output */
void parseBrainfuck(char * input, unsigned long inputLength) {
printf("\n/* BF Source:\n%s\n*/", input);
unsigned long translated_length = inputLength * TRANSLATION_EXPANDER;
char translated[translated_length];
char inChar, nextChar;
unsigned long input_ind = 0, translated_ind = 0;
if (DEBUG)
printf("/*Parsing BF source code of length %lu bytes (%lu bytes reserved):\n%s*/\n", inputLength, translated_length, input);
translated_ind += str_copy(
"\n#include <stdio.h>\n#include <stdlib.h>\n\nint main (void) {\n\tchar o[30000];\n\tint i; for (i = 0; i < 30000; i++) o[i] = 0;\n\ti = 0;\n",
translated + translated_ind
);
for (input_ind = 0; input_ind < inputLength; input_ind++) {
inChar = input[input_ind];
if (input_ind + 1 < inputLength) {
nextChar = input[input_ind +1];
// Ignore empty loops and increments immediately followed by decrements
if ((inChar == '[' && nextChar == ']') || (inChar == '+' && nextChar == '-') ||
(inChar == '-' && nextChar == '+') || (inChar == '<' && nextChar == '>') || (inChar == '>' && nextChar == '<')
) {
input_ind += 1; continue; // jump this and the next character instruction as they achieve nothing.
}
}
if (inChar == '\0') continue;
if (DEBUG) printf("In Char: %c\t", inChar);
if (loopIndent > 0) { // properly indent code within a loop
char * tabs;
tabs = (char *) malloc(loopIndent);
size_t j = 0;
for (j = 0; j < loopIndent; j++) tabs[j] = 9; // \t
translated_ind += str_copy(tabs, translated + translated_ind);
free(tabs);
}
translated_ind += str_copy(codeGen(inChar), translated + translated_ind);
if (DEBUG) printf("Translation Index:\t%03lu\n", translated_ind);
}
translated_ind += str_copy("\n\treturn 0;\n}\n", translated + translated_ind);
printf("%s", translated);
}
unsigned long str_len(char * in) {
unsigned long len = 0;
while (in[len] != '\0') {
len++;
asm("nop"); // requires `-funroll-all-loops` passed to GCC
}
len++;
if (DEBUG) printf("String length: %lu\n", len);
return len;
}
int main(int argc, char **argv) {
if (argc < 3 || argv[1][0] != '-') {
fprintf(stderr, "No Brainfuck input!\n");
fprintf(stderr, "Usage:\t%s [-e|-i] brainfuck_input\n-e\tevaluate input string\n-i\tevaluate input file\n", argv[0]);
return EXIT_FAILURE;
} else if (argv[1][1] == 'e') {
char instream[str_len(argv[2])];
strcpy(instream, argv[2]);
if (DEBUG) printf("/* Received Input:\t%s */\n", instream);
// pass the contents of instream to parseBrainfuck();
parseBrainfuck(instream, str_len(argv[2]));
} else if (argv[1][1] == 'i') {
// open the specified file and copy its contents into instream
char * buffer = 0;
unsigned long length = 0;
size_t bytes_read = 0;
if (DEBUG) printf("/* File to read: %s */\n", argv[2]);
FILE * f = fopen(argv[2], "rb");
if (f) {
fseek(f, 0, SEEK_END); // go to end of file
length = ftell(f); // get the length of the file (in bytes)
if (DEBUG) printf("/*File size: %ld bytes*/\n", length);
buffer = malloc(length);
fseek(f, 0, SEEK_SET); // go to start of file
if (buffer) bytes_read = fread(buffer, 1, length, f);
printf("/* %03lu bytes read from file. */\n", bytes_read);
if (DEBUG) printf("/* File content:\n%s */\n", buffer);
fclose(f);
} else fprintf(stderr, "No input file specified! Did you mean to use -e instead?\n");
if (buffer) parseBrainfuck(buffer, length);
else fprintf(stderr, "No file input in buffer!");
free(buffer);
}
printf("\n");
return EXIT_SUCCESS;
}