mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-06 04:37:47 +08:00
127 lines
3.6 KiB
C
127 lines
3.6 KiB
C
/*
|
||
* Copyright (c) 2020-2021 Thakee Nathees
|
||
* Distributed Under The MIT License
|
||
*/
|
||
|
||
|
||
// The REPL (Read Evaluvate Print Loop) implementation.
|
||
// https://en.wikipedia.org/wiki/Read–eval–print_loop.
|
||
|
||
#include "common.h"
|
||
|
||
#include <ctype.h> // isspace
|
||
#include "utils.h"
|
||
|
||
|
||
// FIXME: use fgetc char by char till reach a new line.
|
||
//
|
||
// Read a line from stdinand returns it without the line ending. Accepting
|
||
// an optional argument [length] (could be NULL). Note that the returned string
|
||
// is heap allocated and should be cleaned by 'free()' function.
|
||
const char* read_line(uint32_t* length) {
|
||
const int size = 1024;
|
||
char* mem = (char*)malloc(size);
|
||
fgets(mem, size, stdin);
|
||
size_t len = strlen(mem);
|
||
|
||
// FIXME: handle \r\n, this is temp.
|
||
mem[len - 1] = '\0';
|
||
if (length != NULL) *length = (uint32_t)(len - 1);
|
||
|
||
return mem;
|
||
}
|
||
|
||
// Returns true if the string is empty, used to check if the input line is
|
||
// empty to skip compilation of empty string.
|
||
static inline bool is_str_empty(const char* line) {
|
||
ASSERT(line != NULL, OOPS);
|
||
|
||
for (const char* c = line; *c != '\0'; c++) {
|
||
if (!isspace(*c)) return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// The main loop of the REPL. will return the exit code.
|
||
int repl(PKVM* vm, const PkCompileOptions* options) {
|
||
|
||
// Set repl_mode of the user_data.
|
||
VmUserData* user_data = (VmUserData*)pkGetUserData(vm);
|
||
user_data->repl_mode = true;
|
||
|
||
// Print the copyright and license notice.
|
||
printf("%s\n", CLI_NOTICE);
|
||
|
||
// The main module that'll used to compile and execute the input source.
|
||
PkHandle* module = pkNewModule(vm, "$(REPL)");
|
||
|
||
// FIXME: Again it's temp for testing.
|
||
|
||
// A buffer to store lines read from stdin.
|
||
ByteBuffer lines;
|
||
byteBufferInit(&lines);
|
||
|
||
// Will be set to true if the compilation failed with unexpected EOF to add
|
||
// more lines to the [lines] buffer.
|
||
bool need_more_lines = false;
|
||
|
||
bool done = false;
|
||
do {
|
||
|
||
// Print the input listening line.
|
||
if (!need_more_lines) {
|
||
printf(">>> ");
|
||
} else {
|
||
printf("... ");
|
||
}
|
||
|
||
// Read a line from stdin and add the line to the lines buffer.
|
||
uint32_t length = 0;
|
||
const char* line = read_line(&length);
|
||
bool is_empty = is_str_empty(line);
|
||
|
||
// If the line is empty and we don't have to compile it.
|
||
if (is_empty && !need_more_lines) {
|
||
free((void*)line);
|
||
ASSERT(lines.count == 0, OOPS);
|
||
continue;
|
||
}
|
||
|
||
if (lines.count != 0) byteBufferWrite(&lines, '\n');
|
||
byteBufferAddString(&lines, line, length);
|
||
free((void*)line);
|
||
byteBufferWrite(&lines, '\0');
|
||
|
||
// Compile the buffer to the module.
|
||
PkStringPtr source_ptr = { (const char*)lines.data, NULL, NULL };
|
||
PkResult result = pkCompileModule(vm, module, source_ptr, options);
|
||
|
||
if (result == PK_RESULT_UNEXPECTED_EOF) {
|
||
ASSERT(lines.count > 0 && lines.data[lines.count - 1] == '\0', OOPS);
|
||
lines.count -= 1; // Remove the null byte to append a new string.
|
||
need_more_lines = true;
|
||
continue;
|
||
}
|
||
|
||
// We're buffering the lines for un expected EOF, if we reached here that
|
||
// means it's either successfully compiled or compilation error. Clean the
|
||
// buffer for the next iteration.
|
||
need_more_lines = false;
|
||
byteBufferClear(&lines);
|
||
|
||
if (result != PK_RESULT_SUCCESS) continue;
|
||
|
||
// Compiled source would be the "main" function of the module. Run it.
|
||
PkHandle* _main = pkGetFunction(vm, module, PK_IMPLICIT_MAIN_NAME);
|
||
PkHandle* fiber = pkNewFiber(vm, _main);
|
||
result = pkRunFiber(vm, fiber, 0, NULL);
|
||
pkReleaseHandle(vm, _main);
|
||
pkReleaseHandle(vm, fiber);
|
||
|
||
} while (!done);
|
||
|
||
pkReleaseHandle(vm, module);
|
||
|
||
return 0;
|
||
|
||
} |