Skip to content

Commit c8740eb

Browse files
committed
first commit
0 parents  commit c8740eb

22 files changed

+1158
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/build/*
2+
/buildwin/*
3+
*.exe
4+
kissfuck

kisscomp.c

+245
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
#include <kissfuck.h>
2+
#include <stdlib.h>
3+
4+
/*
5+
* KISS brainfuck iterpreter.
6+
* Copyright (C) UtoECat 2023. All rights reserved.
7+
* GNU GPL License. No any warranty.
8+
*
9+
* Some testes from the internet was passed :p
10+
*/
11+
12+
/*
13+
* Context allocation
14+
*/
15+
16+
struct kissfuck* makectx() {
17+
void *p = calloc(sizeof(struct kissfuck), 1);
18+
if (!p) {
19+
fprintf(stderr, "Memory allocation error!\n");
20+
return ERR_ERR;
21+
}
22+
return p;
23+
}
24+
25+
void freectx (struct kissfuck* x) {
26+
if (x) free(x);
27+
}
28+
29+
/*
30+
* COMPILING
31+
*/
32+
33+
static void compiler_message(struct kissfuck* x, const char* s) {
34+
fprintf(stderr, "[compiler] : %s (at line %i)\n", s, x->line);
35+
}
36+
37+
#define INDEX_LONG_BC(x, pos) *((uint16_t*)(x->bytecode + (pos)))
38+
39+
static int finallize_jumppair(struct kissfuck* x, uint16_t first, uint16_t second) {
40+
// some assertions first
41+
if (x->bytecode[first] != BC_JPZ) {
42+
return ERR_ERR;
43+
}
44+
if (x->bytecode[second] != BC_JPNZ) {
45+
return ERR_ERR;
46+
}
47+
uint16_t start = first + sizeof(uint16_t) + 1;
48+
uint16_t end = second + sizeof(uint16_t) + 1;
49+
// set jump destinations :D
50+
INDEX_LONG_BC(x, first + 1) = end;
51+
INDEX_LONG_BC(x, second + 1) = start;
52+
return ERR_OK;
53+
}
54+
55+
static int checkchar(FILE* f, char ch) {
56+
return (!feof(f)) && ch;
57+
}
58+
59+
static inline void pushbc1(struct kissfuck* x, enum bytecode b, uint8_t v) {
60+
x->bytecode[x->instp++] = b;
61+
x->bytecode[x->instp++] = v; // constant value
62+
};
63+
64+
static inline void pushbc2(struct kissfuck* x, enum bytecode b, uint16_t v) {
65+
x->bytecode[x->instp++] = b;
66+
*(uint16_t*)(x->bytecode + x->instp) = v; // constant value
67+
x->instp += 2;
68+
};
69+
70+
static inline void closecode(struct kissfuck* x, enum bytecode b, int v) {
71+
if (b == BC_ADD || b == BC_SET) {
72+
if (v == 0) {return;}
73+
else if (v > 0) pushbc1(x, b, v);
74+
else {
75+
v = -v;
76+
pushbc1(x, b, (uint8_t)(UINT8_MAX - v + 1));
77+
}
78+
} else if (b == BC_NEXT) {
79+
if (v == 0) {return;}
80+
else if (v > 0) pushbc2(x, b, v);
81+
else {
82+
v = -v;
83+
pushbc2(x, b, (uint16_t)(UINT16_MAX - v + 1));
84+
}
85+
} else if (b == BC_IN || b == BC_OUT) {
86+
if (v <= 0) {
87+
fprintf(stderr, "Value is below zero or equal; for normal operands :/\n");
88+
exit(-1);
89+
}
90+
pushbc1(x, b, v);
91+
} else if (b == BC_HALT) {
92+
pushbc1(x, b, 0);
93+
}
94+
}
95+
96+
int loadcode(struct kissfuck* x, const char* filename) {
97+
if (x == ERR_ERR) return ERR_ERR;
98+
99+
// init
100+
x->line = 0;
101+
x->instp = 0;
102+
x->cellp = 0;
103+
104+
FILE *f = fopen(filename, "r");
105+
if (!f) {
106+
compiler_message(x, "Can't open file!");
107+
return ERR_ERR;
108+
}
109+
110+
int status = ERR_OK;
111+
enum bytecode oldcode = 90;
112+
int codedata = 0;
113+
uint16_t jmptbl[65536][2] = {0}; // holy shit
114+
uint16_t jmptblpos = 0;
115+
116+
char ch = getc(f);
117+
#define NEXT_CHAR() ch = getc(f);
118+
while (checkchar(f, ch)) {
119+
120+
if (CHAR_COMM(ch)) { // comment
121+
while (getc(f) != '\n') {};
122+
x->line++; NEXT_CHAR();
123+
} else if (VALID_TOKEN(ch)) {
124+
int val= 0;
125+
switch (ch) {
126+
// + and -
127+
case TOKEN_INC :
128+
val = 2;
129+
// falltrough
130+
case TOKEN_DEC :
131+
val--;
132+
if (oldcode != BC_ADD && oldcode != BC_SET) {
133+
closecode(x, oldcode, codedata);
134+
oldcode = BC_ADD;
135+
codedata = 0;
136+
}
137+
codedata += val;
138+
break;
139+
// ,
140+
case TOKEN_IN :
141+
if (oldcode != BC_IN) {
142+
closecode(x, oldcode, codedata);
143+
oldcode = BC_IN;
144+
codedata = 0;
145+
}
146+
codedata += 1;
147+
break;
148+
// .
149+
case TOKEN_OUT :
150+
if (oldcode != BC_OUT) {
151+
closecode(x, oldcode, codedata);
152+
oldcode = BC_OUT;
153+
codedata = 0;
154+
}
155+
codedata += 1;
156+
break;
157+
/*
158+
* TODO: GOOCLE TRANSLATE HELP ME :(
159+
* NEXT and PREV operations are взаимоисключающие like addition and substraction
160+
*/
161+
// > and <
162+
case TOKEN_NEXT :
163+
val = 2;
164+
case TOKEN_PREV :
165+
val--;
166+
if (oldcode != BC_NEXT) {
167+
closecode(x, oldcode, codedata);
168+
oldcode = BC_NEXT;
169+
codedata = 0;
170+
}
171+
codedata += val;
172+
break;
173+
/*
174+
* Loops a bit harder to make ;p
175+
* We need to use jumptable for this
176+
*/
177+
// [
178+
case TOKEN_LOOP :
179+
// close other bytecodes
180+
closecode(x, oldcode, codedata);
181+
oldcode = BC_JPZ;
182+
codedata = 0;
183+
// jump calculations
184+
if (jmptbl[jmptblpos][0]) jmptblpos++;
185+
jmptbl[jmptblpos][0] = x->instp; // add start of the loop to the jumptable
186+
// instruction body will be rewrited later...
187+
pushbc2(x, BC_JPZ, 0);
188+
189+
if (jmptbl[jmptblpos][1]) { // finalized pair
190+
if (finallize_jumppair(x, jmptbl[jmptblpos][0], jmptbl[jmptblpos][1]) != ERR_OK) {
191+
compiler_message(x, "Can't finnalize jump pair! Internal error!");
192+
status = ERR_ERR; goto end_it;
193+
}
194+
jmptbl[jmptblpos][0] = 0; jmptbl[jmptblpos][1] = 0; // cleanup :p
195+
uint16_t npos = jmptblpos + 1;
196+
if (jmptbl[npos][0] || jmptbl[npos][1]) jmptblpos = npos; // important
197+
}
198+
break;
199+
// ]
200+
case TOKEN_POOL :
201+
// close other bytecodes
202+
closecode(x, oldcode, codedata);
203+
oldcode = BC_JPNZ;
204+
codedata = 0;
205+
// jump calculations
206+
if (jmptbl[jmptblpos][1]) jmptblpos--;
207+
jmptbl[jmptblpos][1] = x->instp; // add end of the loop to the jumptable
208+
// instruction body will be rewrited later...
209+
pushbc2(x, BC_JPNZ, 0);
210+
211+
if (jmptbl[jmptblpos][0]) { // finalized pair
212+
if (finallize_jumppair(x, jmptbl[jmptblpos][0], jmptbl[jmptblpos][1]) != ERR_OK) {
213+
compiler_message(x, "Can't finnalize jump pair! Internal error!");
214+
status = ERR_ERR; goto end_it;
215+
}
216+
jmptbl[jmptblpos][0] = 0; jmptbl[jmptblpos][1] = 0; // cleanup :p
217+
uint16_t npos = jmptblpos - 1;
218+
if (jmptbl[npos][0] || jmptbl[npos][1]) jmptblpos = npos; // important
219+
}
220+
break;
221+
default:
222+
compiler_message(x, "impossible!");
223+
status = ERR_ERR;
224+
goto end_it;
225+
break;
226+
};NEXT_CHAR();} else if (ch == '\n' || ch == '\r') {
227+
NEXT_CHAR();
228+
x->line++;
229+
} else NEXT_CHAR();
230+
};
231+
closecode(x, oldcode, codedata);
232+
233+
if (jmptblpos != 0) {
234+
compiler_message(x, "Code is not balanced!");
235+
fprintf(stderr, "[compiler] : unused branches count %i!\n", jmptblpos);
236+
status = ERR_ERR;
237+
goto end_it;
238+
}
239+
240+
end_it :
241+
fclose(f);
242+
return status;
243+
}
244+
245+

kissfuck.c

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#include <kissfuck.h>
2+
3+
/*
4+
* KISS brainfuck iterpreter.
5+
* Copyright (C) UtoECat 2023. All rights reserved.
6+
* GNU GPL License. No any warranty.
7+
*
8+
* Some testes from the internet was passed :p
9+
*/
10+
11+
/*
12+
* text :p
13+
*/
14+
15+
#define STR_VERSION "kissfuck v.1.0\n"
16+
17+
#define STR_ABOUT \
18+
"kissfuck - KISS brainfuck interpreter.\n"\
19+
"Coptyright (C) UtoECat 2023. All rights reserved!\n"\
20+
"GNU GPL License. NO ANY WARRIANTY!\n"\
21+
"Github : https://"
22+
23+
#define STR_USAGE "\tUSAGE :\n"\
24+
" $ %s [-h to help]\n $ %s [-v to version]\n"\
25+
" $ %s [filename]\n $ %s [-d to debug] [filename]\n"
26+
27+
#define STR_HELP \
28+
"\tLANGUAGE :\n"\
29+
"Brainfuck operations are '+', '-', '[', ']', '<', '>', '.', ','\n"\
30+
"+\tIncrement value in current cell\n"\
31+
"-\tDecrement value in current cell\n"\
32+
",\tRead character from stdin to current cell\n"\
33+
".\tWrite character to stdout from currnt cell\n"\
34+
"[\tWhile current cell is not 0 loop\n"\
35+
"]\tRepeats loop if current cell is not 0\n"\
36+
"<\tChanges cell to previous\n"\
37+
">\tChanges cell to next\n"
38+
39+
static const char* default_filename = "./test.bfk";
40+
static const char* argv0 = "NULL";
41+
42+
static inline void print_usage(int err) {
43+
if (err == 2) fprintf(stderr, "Bad Usage! ");
44+
if (err == 3) fprintf(stderr, "Bad Flag! ");
45+
if (err == 4) fprintf(stderr, " -- flags are not allowed! ");
46+
fprintf(stderr, STR_USAGE, argv0, argv0, argv0, argv0);
47+
}
48+
49+
static inline void print_help() {
50+
fprintf(stderr, "%s%s", STR_ABOUT, STR_VERSION);
51+
print_usage(0);
52+
fprintf(stderr, "%s", STR_HELP);
53+
}
54+
55+
int debug = 0;
56+
57+
static int parse_argv(int argc, char** argv) {
58+
argv0 = argv[0];
59+
60+
// nothing to parse!
61+
if (argc < 2) return ERR_OK;
62+
63+
if (argc > 3) {
64+
print_usage(2);
65+
return ERR_ERR;
66+
}
67+
68+
int i = 1;
69+
repeatit:
70+
if (argv[i] == NULL) return ERR_OK;
71+
72+
if (argv[i][0] != '-') { // set file
73+
default_filename = argv[i];
74+
return ERR_OK;
75+
} else switch (argv[i][1]) { // parse flag
76+
case '-' : print_usage(4); return ERR_ERR;
77+
case 'h' : print_help(); return ERR_ERR;
78+
case 'd' :
79+
debug = 1; fprintf(stderr, "[vm] : debug mode enabled!\n");
80+
i++;
81+
goto repeatit;
82+
break;
83+
case 'v' : fprintf(stderr, STR_VERSION); return ERR_ERR;
84+
default : print_usage(3); return ERR_ERR;
85+
}
86+
return ERR_OK;
87+
88+
}
89+
90+
int main(int argc, char** argv) {
91+
if (parse_argv(argc, argv) != ERR_OK) return -1;
92+
struct kissfuck* K = makectx();
93+
if (K == ERR_ERR) return -2;
94+
if (loadcode(K, default_filename) != ERR_OK) {
95+
fprintf(stderr, "Show bytecode Dump? (y/n) : ");
96+
if (debug || getchar() == 'y') dumpcode(K);
97+
return -3;
98+
}
99+
stopcode(K);
100+
if (execcode(K) != ERR_OK) {
101+
fprintf(stderr, "Show bytecode Dump? (y/n) : ");
102+
if (debug || getchar() == 'y') dumpcode(K);
103+
}
104+
if (debug) dumpcode(K);
105+
return 0;
106+
}

0 commit comments

Comments
 (0)