|
| 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 | + |
0 commit comments