1
1
#include <kissfuck.h>
2
+ #include <stddef.h>
2
3
#include <stdlib.h>
4
+ #include <assert.h>
3
5
4
6
/*
5
- * KISS brainfuck iterpreter.
7
+ * KISS brainfuck iterpreter. Compiler to the bytecode.
6
8
* Copyright (C) UtoECat 2023. All rights reserved.
7
9
* GNU GPL License. No any warranty.
8
10
*
13
15
* Context allocation
14
16
*/
15
17
18
+
16
19
struct kissfuck * makectx () {
17
20
void * p = calloc (sizeof (struct kissfuck ), 1 );
18
21
if (!p ) {
@@ -30,44 +33,98 @@ void freectx (struct kissfuck* x) {
30
33
* COMPILING
31
34
*/
32
35
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)))
36
+ struct compstate {
37
+ uint8_t * bytecode ;
38
+ uint16_t instp ; // instruction pointer
39
+ uint16_t jmptbl [65536 ][2 ];
40
+ uint16_t jmptblpos ;
41
+ enum bytecode currcode ;
42
+ int codedata ;
43
+ int line ;
44
+ };
38
45
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
- }
46
+ #define NEXT_CHAR () ch = getc(f);
54
47
55
- static int checkchar ( FILE * f , char ch ) {
56
- return (! feof ( f )) && ch ;
48
+ static void compiler_message ( struct compstate * x , const char * s ) {
49
+ fprintf ( stderr , "[compiler] : %s (at line %i)\n" , s , x -> line ) ;
57
50
}
58
51
59
- static inline void pushbc1 (struct kissfuck * x , enum bytecode b , uint8_t v ) {
52
+ // sets instruction with short argument (8 bits)
53
+ static inline void pushbc1 (struct compstate * x , enum bytecode b , uint8_t v ) {
60
54
x -> bytecode [x -> instp ++ ] = b ;
61
55
x -> bytecode [x -> instp ++ ] = v ; // constant value
62
56
};
63
57
64
- static inline void pushbc2 (struct kissfuck * x , enum bytecode b , uint16_t v ) {
58
+ // sets instruction with long argument (16 bits, 1 byte alligment)
59
+ static inline void pushbc2 (struct compstate * x , enum bytecode b , uint16_t v ) {
65
60
x -> bytecode [x -> instp ++ ] = b ;
66
61
* (uint16_t * )(x -> bytecode + x -> instp ) = v ; // constant value
67
62
x -> instp += 2 ;
68
63
};
69
64
70
- static inline void closecode (struct kissfuck * x , enum bytecode b , int v ) {
65
+ // links jumps instructions to each other (on instruction below each other)
66
+ static int finallize_jumppair (struct compstate * x , uint16_t f , uint16_t s ) {
67
+ // next instructions after JPZ and JPNZ instructions
68
+ uint16_t start = f + sizeof (uint16_t ) + 1 ;
69
+ uint16_t end = s + sizeof (uint16_t ) + 1 ;
70
+ // set jump distanations
71
+ * (uint16_t * )(x -> bytecode + f + 1 ) = end ;
72
+ * (uint16_t * )(x -> bytecode + s + 1 ) = start ;
73
+ return ERR_OK ;
74
+ }
75
+
76
+ // tries to finnalize jumppair (returns 1 on success)
77
+ static int trytofinnalize (struct compstate * x ) {
78
+ uint16_t * start = & x -> jmptbl [x -> jmptblpos ][1 ];
79
+ uint16_t * end = & x -> jmptbl [x -> jmptblpos ][0 ];
80
+
81
+ if (* start && * end ) {
82
+ // table can be finnalized!
83
+ assert (finallize_jumppair (x , * start , * end ) == ERR_OK );
84
+ * start = 0 ;
85
+ * end = 0 ;
86
+ return 1 ;
87
+ }
88
+ return 0 ;
89
+ }
90
+
91
+ static void addjumpstart (struct compstate * x ) {
92
+ // if jump table is busy
93
+ if (x -> jmptbl [x -> jmptblpos ][0 ]) x -> jmptblpos ++ ;
94
+ // add start of the loop to the jumptable
95
+ x -> jmptbl [x -> jmptblpos ][0 ] = x -> instp ;
96
+
97
+ // instruction will be rewrited later
98
+ pushbc2 (x , BC_JPZ , 0 );
99
+
100
+ if (trytofinnalize (x )) {
101
+ // move forward if next jumpfield is not empity
102
+ uint16_t npos = x -> jmptblpos + 1 ;
103
+ if (x -> jmptbl [npos ][0 ] || x -> jmptbl [npos ][1 ]) x -> jmptblpos = npos ;
104
+ }
105
+ }
106
+
107
+ static void addjumpend (struct compstate * x ) {
108
+ // if jump table is busy
109
+ if (x -> jmptbl [x -> jmptblpos ][1 ]) x -> jmptblpos -- ;
110
+ // add start of the loop to the jumptable
111
+ x -> jmptbl [x -> jmptblpos ][1 ] = x -> instp ;
112
+
113
+ // instruction data will be rewrited later
114
+ pushbc2 (x , BC_JPNZ , 0 );
115
+
116
+ if (trytofinnalize (x )) {
117
+ // move backward if next jumpfield is not empity
118
+ uint16_t npos = x -> jmptblpos - 1 ;
119
+ if (x -> jmptbl [npos ][0 ] || x -> jmptbl [npos ][1 ]) x -> jmptblpos = npos ;
120
+ }
121
+ }
122
+
123
+ // finnaly adds instruction to bytecode
124
+ static void newcode (struct compstate * x , enum bytecode n ) {
125
+ enum bytecode b = x -> currcode ;
126
+ int v = x -> codedata ;
127
+
71
128
if (b == BC_ADD || b == BC_SET ) {
72
129
if (v == 0 ) {return ;}
73
130
else if (v > 0 ) pushbc1 (x , b , v );
@@ -83,156 +140,105 @@ static inline void closecode(struct kissfuck* x, enum bytecode b, int v) {
83
140
pushbc2 (x , b , (uint16_t )(UINT16_MAX - v + 1 ));
84
141
}
85
142
} 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
- }
143
+ assert (v > 0 && "Value is belowequal zero" );
90
144
pushbc1 (x , b , v );
91
145
} else if (b == BC_HALT ) {
92
146
pushbc1 (x , b , 0 );
93
147
}
148
+
149
+ x -> currcode = n ;
150
+ x -> codedata = 0 ;
94
151
}
95
152
96
- int loadcode (struct kissfuck * x , const char * filename ) {
97
- if (x == ERR_ERR ) return ERR_ERR ;
153
+ /*
154
+ * Compilation functions :p
155
+ */
156
+ int loadcode (struct kissfuck * kf , const char * filename ) {
157
+ if (kf == ERR_ERR ) return ERR_ERR ;
158
+ int status = ERR_OK ;
159
+ char ch ;
160
+
161
+ struct compstate state = {0 };
162
+ state .currcode = 128 ;
163
+ struct compstate * x = & state ;
164
+ x -> bytecode = kf -> bytecode ;
98
165
99
166
// init
100
- x -> line = 0 ;
101
- x -> instp = 0 ;
102
- x -> cellp = 0 ;
167
+ stopcode (kf );
103
168
104
169
FILE * f = fopen (filename , "r" );
105
170
if (!f ) {
106
- compiler_message ( x , "Can 't open file!" );
171
+ fprintf ( stderr , "[compiler] : can 't open file %s!\n" , filename );
107
172
return ERR_ERR ;
108
173
}
109
174
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 )) {
175
+ while ((ch = getc (f )) && !feof (f )) {
119
176
120
177
if (CHAR_COMM (ch )) { // comment
121
178
while (getc (f ) != '\n' ) {};
122
- x -> line ++ ; NEXT_CHAR ();
179
+ x -> line ++ ;
123
180
} else if (VALID_TOKEN (ch )) {
124
- int val = 0 ;
181
+ int val = 0 ;
125
182
switch (ch ) {
126
183
// + and -
127
184
case TOKEN_INC :
128
- val = 2 ;
129
- // falltrough
185
+ val = 2 ;
186
+ _fallthrough ()
130
187
case TOKEN_DEC :
131
188
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 ;
189
+ if (x -> currcode != BC_ADD && x -> currcode != BC_SET ) newcode (x , BC_ADD );
190
+ x -> codedata += val ;
138
191
break ;
139
192
// ,
140
193
case TOKEN_IN :
141
- if (oldcode != BC_IN ) {
142
- closecode (x , oldcode , codedata );
143
- oldcode = BC_IN ;
144
- codedata = 0 ;
145
- }
146
- codedata += 1 ;
194
+ if (x -> currcode != BC_IN ) newcode (x , BC_IN );
195
+ x -> codedata += 1 ;
147
196
break ;
148
197
// .
149
198
case TOKEN_OUT :
150
- if (oldcode != BC_OUT ) {
151
- closecode (x , oldcode , codedata );
152
- oldcode = BC_OUT ;
153
- codedata = 0 ;
154
- }
155
- codedata += 1 ;
199
+ if (x -> currcode != BC_OUT ) newcode (x , BC_OUT );
200
+ x -> codedata += 1 ;
156
201
break ;
157
- /*
158
- * TODO: GOOCLE TRANSLATE HELP ME :(
159
- * NEXT and PREV operations are взаимоисключающие like addition and substraction
160
- */
202
+ // NEXT and PREV operations are mutually exclusive
203
+ // operations like addition and substraction
161
204
// > and <
162
205
case TOKEN_NEXT :
163
206
val = 2 ;
207
+ _fallthrough ()
164
208
case TOKEN_PREV :
165
209
val -- ;
166
- if (oldcode != BC_NEXT ) {
167
- closecode (x , oldcode , codedata );
168
- oldcode = BC_NEXT ;
169
- codedata = 0 ;
170
- }
171
- codedata += val ;
210
+ if (x -> currcode != BC_NEXT ) newcode (x , BC_NEXT );
211
+ x -> codedata += val ;
172
212
break ;
173
- /*
174
- * Loops a bit harder to make ;p
175
- * We need to use jumptable for this
176
- */
213
+ // Loops a bit harder to make ;p
214
+ // We need to use jumptable for this
177
215
// [
178
216
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
- }
217
+ newcode (x , BC_JPZ ); // close other bytecodes
218
+ addjumpstart (x ); // save code position to the jump table
198
219
break ;
199
220
// ]
200
221
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
222
+ newcode (x , BC_JPNZ ); // close other bytecodes
223
+ addjumpend (x ); // save code position to the jumptable
208
224
// instruction body will be rewrited later...
209
225
pushbc2 (x , BC_JPNZ , 0 );
210
226
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
227
break ;
221
228
default :
222
229
compiler_message (x , "impossible!" );
223
230
status = ERR_ERR ;
224
231
goto end_it ;
225
232
break ;
226
- };NEXT_CHAR ();} else if (ch == '\n' || ch == '\r' ) {
227
- NEXT_CHAR ();
233
+ }} else if (ch == '\n' || ch == '\r' ) {
228
234
x -> line ++ ;
229
- } else NEXT_CHAR () ;
235
+ };
230
236
};
231
- closecode (x , oldcode , codedata );
237
+ newcode (x , 128 );
232
238
233
- if (jmptblpos != 0 ) {
239
+ if (x -> jmptblpos != 0 ) {
234
240
compiler_message (x , "Code is not balanced!" );
235
- fprintf (stderr , "[compiler] : unused branches count %i!\n" , jmptblpos );
241
+ fprintf (stderr , "[compiler] : unused branches count %i!\n" , x -> jmptblpos );
236
242
status = ERR_ERR ;
237
243
goto end_it ;
238
244
}
0 commit comments