kuroko/kuroko.c

202 lines
4.9 KiB
C
Raw Normal View History

2021-01-02 06:21:11 +03:00
/**
* Kuroko interpreter main executable.
*
* Reads lines from stdin with the `rline` library and executes them,
* or executes scripts from the argument list.
*/
#include <stdio.h>
2020-12-28 04:54:25 +03:00
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
2021-01-02 06:21:11 +03:00
#ifdef __toaru__
#include <toaru/rline.h>
#include <kuroko.h>
#else
#include "rline.h"
#include "kuroko.h"
2021-01-02 06:21:11 +03:00
#endif
#include "chunk.h"
#include "debug.h"
#include "vm.h"
2020-12-28 04:54:25 +03:00
#include "memory.h"
2020-12-28 05:37:38 +03:00
int main(int argc, char * argv[]) {
int flags = 0;
int opt;
while ((opt = getopt(argc, argv, "tdgs")) != -1) {
switch (opt) {
case 't':
2021-01-02 06:21:11 +03:00
/* Disassemble instructions as they are executed. */
flags |= KRK_ENABLE_TRACING;
break;
case 'd':
2021-01-02 06:21:11 +03:00
/* Disassemble code blocks after compilation. */
flags |= KRK_ENABLE_DISASSEMBLY;
break;
2020-12-28 04:54:25 +03:00
case 's':
2021-01-02 06:21:11 +03:00
/* Print debug information during compilation. */
flags |= KRK_ENABLE_SCAN_TRACING;
2020-12-28 04:54:25 +03:00
break;
case 'g':
2021-01-02 06:21:11 +03:00
/* Always garbage collect during an allocation. */
flags |= KRK_ENABLE_STRESS_GC;
break;
}
}
krk_initVM(flags);
2020-12-28 04:54:25 +03:00
KrkValue result = INTEGER_VAL(0);
if (optind == argc) {
2020-12-28 04:54:25 +03:00
/* Run the repl */
int exit = 0;
2021-01-02 06:21:11 +03:00
/* Set ^D to send EOF */
2020-12-28 05:37:38 +03:00
rline_exit_string="";
2021-01-02 06:21:11 +03:00
/* Enable syntax highlight for Kuroko */
2020-12-28 15:31:46 +03:00
rline_exp_set_syntax("krk");
2021-01-02 06:21:11 +03:00
/* TODO: Add tab completion for globals, known fields/methods... */
2020-12-28 05:37:38 +03:00
//rline_exp_set_tab_complete_func(tab_complete_func);
2020-12-28 04:54:25 +03:00
while (!exit) {
size_t lineCapacity = 8;
size_t lineCount = 0;
char ** lines = ALLOCATE(char *, lineCapacity);
size_t totalData = 0;
int valid = 1;
char * allData = NULL;
int inBlock = 0;
2020-12-28 05:37:38 +03:00
int blockWidth = 0;
2020-12-28 04:54:25 +03:00
2021-01-02 06:21:11 +03:00
/* Main prompt is >>> like in Python */
2020-12-28 05:37:38 +03:00
rline_exp_set_prompts(">>> ", "", 4, 0);
2020-12-28 04:54:25 +03:00
while (1) {
2020-12-28 05:37:38 +03:00
/* This would be a nice place for line editing */
char buf[4096] = {0};
2020-12-28 04:54:25 +03:00
if (inBlock) {
2021-01-02 06:21:11 +03:00
/* When entering multiple lines, the additional lines
* will show a single > (and keep the left side aligned) */
2020-12-28 05:37:38 +03:00
rline_exp_set_prompts(" > ", "", 4, 0);
2021-01-02 06:21:11 +03:00
/* Also add indentation as necessary */
2020-12-28 05:37:38 +03:00
rline_preload = malloc(blockWidth + 1);
for (int i = 0; i < blockWidth; ++i) {
rline_preload[i] = ' ';
}
rline_preload[blockWidth] = '\0';
2020-12-28 04:54:25 +03:00
}
2020-12-28 08:00:12 +03:00
rline_scroll = 0;
2020-12-28 05:37:38 +03:00
if (rline(buf, 4096) == 0) {
valid = 0;
2020-12-28 04:54:25 +03:00
exit = 1;
break;
}
if (buf[strlen(buf)-1] != '\n') {
2021-01-02 06:21:11 +03:00
/* rline shouldn't allow this as it doesn't accept ^D to submit input
* unless the line is empty, but just in case... */
2020-12-28 04:54:25 +03:00
fprintf(stderr, "Expected end of line in repl input. Did you ^D early?\n");
valid = 0;
break;
}
2021-01-02 06:21:11 +03:00
2020-12-28 04:54:25 +03:00
if (lineCapacity < lineCount + 1) {
2021-01-02 06:21:11 +03:00
/* If we need more space, grow as needed... */
2020-12-28 04:54:25 +03:00
size_t old = lineCapacity;
lineCapacity = GROW_CAPACITY(old);
lines = GROW_ARRAY(char *,lines,old,lineCapacity);
}
int i = lineCount++;
lines[i] = strdup(buf);
2020-12-28 04:54:25 +03:00
size_t lineLength = strlen(lines[i]);
totalData += lineLength;
2021-01-02 06:21:11 +03:00
/* Figure out indentation */
int isSpaces = 1;
int countSpaces = 0;
2020-12-28 05:11:50 +03:00
for (size_t j = 0; j < lineLength; ++j) {
2020-12-28 05:37:38 +03:00
if (lines[i][j] != ' ' && lines[i][j] != '\n') {
2021-01-02 06:21:11 +03:00
isSpaces = 0;
2020-12-28 05:37:38 +03:00
break;
}
2021-01-02 06:21:11 +03:00
countSpaces += 1;
2020-12-28 05:37:38 +03:00
}
2021-01-02 06:21:11 +03:00
/* Naively detect the start of a new block so we can
* continue to accept input. Our compiler isn't really
* set up to let us compile "on the fly" so we can't just
* run lines through it and see if it wants more... */
2020-12-28 05:37:38 +03:00
if (lineLength > 2 && lines[i][lineLength-2] == ':') {
2020-12-28 04:54:25 +03:00
inBlock = 1;
2021-01-02 06:21:11 +03:00
blockWidth = countSpaces + 4;
2020-12-28 05:37:38 +03:00
continue;
} else if (inBlock && lineLength != 1) {
2021-01-02 06:21:11 +03:00
if (isSpaces) {
2020-12-28 05:37:38 +03:00
free(lines[i]);
totalData -= lineLength;
lineCount--;
break;
}
2021-01-02 06:21:11 +03:00
blockWidth = countSpaces;
2020-12-28 04:54:25 +03:00
continue;
} else if (lineLength > 1 && lines[i][countSpaces] == '@') {
inBlock = 1;
blockWidth = countSpaces;
continue;
2020-12-28 04:54:25 +03:00
}
2021-01-02 06:21:11 +03:00
/* Ignore blank lines. */
if (isSpaces) valid = 0;
/* If we're not in a block, or have entered a blank line,
* we can stop reading new lines and jump to execution. */
2020-12-28 04:54:25 +03:00
break;
}
if (valid) {
allData = malloc(totalData + 1);
2020-12-28 04:54:25 +03:00
allData[0] = '\0';
}
2021-01-02 06:21:11 +03:00
2020-12-28 05:11:50 +03:00
for (size_t i = 0; i < lineCount; ++i) {
2020-12-28 04:54:25 +03:00
if (valid) strcat(allData, lines[i]);
2020-12-28 06:16:44 +03:00
rline_history_insert(strdup(lines[i]));
2020-12-28 04:54:25 +03:00
free(lines[i]);
}
FREE_ARRAY(char *, lines, lineCapacity);
if (valid) {
KrkValue result = krk_interpret(allData, 0, "<module>","<stdin>");
if (!IS_NONE(result)) {
fprintf(stdout, " \033[1;30m=> ");
krk_printValue(stdout, result);
fprintf(stdout, "\033[0m\n");
}
}
}
} else {
2021-01-02 06:21:11 +03:00
/* Expect the rest of the arguments to be scripts to run;
* collect the result of the last one and use it as the
* exit code if it's an integer. */
2020-12-28 04:54:25 +03:00
for (int i = optind; i < argc; ++i) {
KrkValue out = krk_runfile(argv[i],0,"<module>",argv[i]);
if (i + 1 == argc) result = out;
}
}
krk_freeVM();
if (IS_INTEGER(result)) return AS_INTEGER(result);
return 0;
}