main soggy/cotton / src / interpreter.c
  1// i got to eventually clean up this file a bit i feel like its very janky but i
  2// suppose i can worry about that later
  3
  4#include "interpreter.h"
  5#include "cottonwindow.h"
  6#include "eiki.h"
  7#include <SDL2/SDL.h>
  8#include <stdio.h>
  9#include <stdlib.h>
 10#include <string.h>
 11#include <unistd.h>
 12
 13// maximum length of a .cot file line
 14#define MAX_LINE 256
 15
 16// da string AND variable stuff
 17
 18static void 
 19strip_newline(char *s) 
 20{
 21     s[strcspn(s, "\r\n")] = 0; 
 22}
 23
 24static char 
 25*get_arg (char *line)
 26{
 27    char *space = strchr(line, ' ');
 28
 29    if (space != NULL) {
 30        return space + 1;
 31    }
 32
 33    else {
 34        return NULL;
 35    }
 36}
 37
 38static char 
 39*strip_quotes(char *s)
 40{
 41    if (s[0] == '"') {
 42        char *end = strrchr(s, '"');
 43
 44        if (end == NULL) {
 45            fprintf(stderr, "cot syntax error : looks like you forgot a quote at the end xP\n");
 46            return s;
 47        }
 48
 49        *end = '\0';
 50        return s + 1;
 51    }
 52
 53    return s;
 54}
 55
 56static const char *get_var_value
 57(Cotton *cotton, const char *name)
 58{
 59    for (int i = 0; i < cotton->var_count; i++) {
 60        if (strcmp(cotton->vars[i].name, name) == 0) {
 61            return cotton->vars[i].value;
 62        }
 63    }
 64    return NULL;
 65}
 66
 67// da graphics stuff
 68
 69static void draw_pixel
 70(Cotton *cotton, int x, int y)
 71{
 72    cotton->video[y * VIDEO_WIDTH + x] = cotton->c_cottolette;
 73}
 74
 75static void draw_char
 76(Cotton *cotton, char c, int x, int y)
 77{
 78    for (int row = 0; row < FONT_HEIGHT; row++) {
 79        uint8_t bits = EIKI_FONT[(uint8_t)c][row];
 80
 81        for (int col = 0; col < FONT_WIDTH; col++) {
 82
 83            if (bits & (1 << col)) {
 84                draw_pixel(cotton, x + col, y + row);
 85            }
 86        }
 87    }
 88}
 89
 90// da commands
 91
 92static void 
 93cmd_print(Cotton *cotton, CottonWindow *cw, char *arg)
 94{
 95    if (!arg)
 96        return;
 97
 98    char *text = strip_quotes(arg);
 99
100    const char *value = get_var_value(cotton, text);
101    if (value != NULL) {
102        text = (char *)value;
103    }
104
105    printf("%s\n", text);
106
107    for (int i = 0; text[i] != '\0'; i++) {
108        if (text[i] == '\n') {
109            cotton->cursor_x = 0;
110            cotton->cursor_y += FONT_HEIGHT + 4;
111            continue;
112        }
113
114        draw_char(cotton, text[i], cotton->cursor_x, cotton->cursor_y);
115        cotton->cursor_x += FONT_WIDTH + 2;
116
117        if (cotton->cursor_x + FONT_WIDTH + 2 >= VIDEO_WIDTH) {
118            cotton->cursor_x = 0;
119            cotton->cursor_y += FONT_HEIGHT + 4;
120        }
121    }
122
123    cotton->cursor_x = 0;
124    cotton->cursor_y += FONT_HEIGHT + 4;
125
126    cottonwindow_update(cw, cotton->video, sizeof(cotton->video[0]) * VIDEO_WIDTH);
127}
128
129static void 
130cmd_var(Cotton *cotton, char *arg)
131{
132    if (!arg)
133        return;
134
135    char *space = strchr(arg, ' ');
136    if (!space) {
137        fprintf(stderr, "cot syntax error: you forgor to put a value for your variable xP\n");
138        return;
139    }
140
141    *space = '\0';
142    char *name = arg;
143    char *value = space + 1;
144
145    cotton_store_var(cotton, name, value);
146}
147
148static void 
149cmd_clear(Cotton *cotton, CottonWindow *cw)
150{
151    for (int i = 0; i < VIDEO_WIDTH * VIDEO_HEIGHT; i++) {
152        cotton->video[i] = 0;
153    }
154
155    cotton->cursor_x = 0;
156    cotton->cursor_y = 0;
157
158    cottonwindow_update(cw, cotton->video,
159                        sizeof(cotton->video[0]) * VIDEO_WIDTH);
160}
161
162static void 
163cmd_color(Cotton *cotton, char *arg)
164{
165    if (!arg)
166        return;
167
168    if (strcmp(arg, "black") == 0)
169        cotton->c_cottolette = COTTOLETTE[0];
170
171    else if (strcmp(arg, "white") == 0)
172        cotton->c_cottolette = COTTOLETTE[1];
173
174    else if (strcmp(arg, "rey") == 0)
175        cotton->c_cottolette = COTTOLETTE[2];
176
177    else if (strcmp(arg, "wist") == 0)
178        cotton->c_cottolette = COTTOLETTE[3];
179
180    else if (strcmp(arg, "wink") == 0)
181        cotton->c_cottolette = COTTOLETTE[4];
182
183    else if (strcmp(arg, "laven") == 0)
184        cotton->c_cottolette = COTTOLETTE[5];
185
186    else if (strcmp(arg, "geen") == 0)
187        cotton->c_cottolette = COTTOLETTE[6];
188
189    else
190        fprintf(stderr,
191                "cot syntax error: cotton doesn't know the color \"%s\" "
192                "sorry,, :(\n",
193                arg);
194}
195
196static void 
197cmd_wait(Cotton *cotton, char *arg)
198{
199    if (!arg)
200        return;
201
202    int seconds = atoi(arg);
203
204    cotton->ticktock = SDL_GetTicks() + (seconds * 1000);
205}
206
207static void 
208cmd_input(Cotton *cotton, char *arg)
209{
210    if (!arg) {
211        fprintf(stderr,
212                "cotton syntax error: input needs a variable name xP\n");
213        return;
214    }
215
216    char buffer[VAR_VAL_LEN];
217
218    // kind of a quick and dirty way of implementing input, i know, but, i cant be arsed to get myself into a potential rabbit hole with SDL so for now i  will leave this to be this way,, sorry,,
219    if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
220        buffer[strcspn(buffer, "\r\n")] = '\0';
221
222        cotton_store_var(cotton, arg, buffer);
223    }
224}
225
226static void 
227cmd_kill(void)
228{
229    printf("[cotton stopped]\n");
230    exit(0);
231}
232
233static void 
234cmd_math(Cotton *cotton, char *arg, char op)
235{
236    char var_name[64];
237    char value[64];
238
239    if (!arg || sscanf(arg, "%63s %63s", var_name, value) != 2) {
240        fprintf(stderr, "cot syntax error: usage is '<add, sub, mult, div> <variable> <number OR another variable>'\n");
241        return;
242    }
243
244    int current = 0;
245    int amount = 0;
246
247    // thank you clang for coming in clutch and telling me to make this const i forgor (skull emoji)
248    const char *var = get_var_value(cotton, var_name);
249    if (var) {
250        current = atoi(var);
251    }
252
253    // and this too, i love clang, clang is the best compiler evah !!!
254    const char *other = get_var_value(cotton, value);
255    if (other) {
256        amount = atoi(other);
257    } else {
258        amount = atoi(value);
259    }
260
261    switch (op) {
262        case '+':
263            current += amount;
264            break;
265
266        case '-':
267            current -= amount;
268            break;
269
270        case '*':
271            current *= amount;
272            break;
273
274        case '/':        
275            current /= amount;
276            break;
277
278        default:
279            fprintf(stderr, "cot syntax error: cot doesn't know what operator this is :( \n");
280            return;
281    }
282
283    char buffer[32];
284    sprintf(buffer, "%d", current);
285
286    cotton_store_var(cotton, var_name, buffer);
287}
288
289static void 
290cmd_unknown(const char *cmd)
291{
292    fprintf(stderr,
293            "cot syntax error: cotton doesn't know what \"%s\" means, care to "
294            "type that out again? :<\n",
295            cmd);
296}
297
298// da interpreter loop
299
300void 
301cotton_interpret(Cotton *cotton, CottonWindow *cw, FILE *file)
302{
303    char line[MAX_LINE];
304
305    // this reads the file one line at a time until we get to da end of da file
306    if (!fgets(line, sizeof(line), file))
307        return;
308
309    strip_newline(line);
310
311    // cot shall use # comments like C because C is cool and i love C so bwaaa
312    if (line[0] == '\0' || line[0] == '#')
313        return;
314
315    char line_copy[MAX_LINE];
316
317    strncpy(line_copy, line, MAX_LINE - 1);
318    line_copy[MAX_LINE - 1] = '\0';
319
320    char *cmd = strtok(line_copy, " ");
321
322    if (!cmd)
323        return;
324
325    // RELEASE THE KRAKE- I MEAN, RELEASE THE GREAT WALL OF COMMANDS !!!
326    // could i format this better? maybe,, but but i'd say this is readable and
327    // i think readability is gud so i will keep it nice and green,,, green!! :D
328    char *arg = get_arg(line);
329
330    if (strcmp(cmd, "print") == 0) {
331        cmd_print(cotton, cw, arg);
332    }
333
334    else if (strcmp(cmd, "var") == 0) {
335        cmd_var(cotton, arg);
336    }
337
338    else if (strcmp(cmd, "clear") == 0) {
339        cmd_clear(cotton, cw);
340    }
341
342    else if (strcmp(cmd, "color") == 0) {
343        cmd_color(cotton, arg);
344    }
345
346    else if (strcmp(cmd, "wait") == 0) {
347        cmd_wait(cotton, arg);
348    }
349
350    else if (strcmp(cmd, "input") == 0) {
351        cmd_input(cotton, arg);
352    }
353
354    else if (strcmp(cmd, "kill") == 0) {
355        cmd_kill();
356    }
357
358    else if (strcmp(cmd, "add") == 0) {
359        cmd_math(cotton, arg, '+');
360    }
361
362    else if (strcmp(cmd, "sub") == 0) {
363        cmd_math(cotton, arg, '-');
364    }
365
366    else if (strcmp(cmd, "mult") == 0) {
367        cmd_math(cotton, arg, '*');
368    }
369
370    else if (strcmp(cmd, "div") == 0) {
371        cmd_math(cotton, arg, '/');
372    }
373
374    else {
375        cmd_unknown(cmd);
376    }
377}