pocketlang/cli/main.c

241 lines
6.1 KiB
C
Raw Normal View History

2021-02-07 15:40:00 +08:00
/*
* Copyright (c) 2021 Thakee Nathees
* Licensed under: MIT License
*/
2021-02-17 02:28:03 +08:00
#include <stdlib.h>
2021-05-22 21:27:40 +08:00
#include <string.h>
2021-02-07 15:40:00 +08:00
#include <stdio.h>
2021-05-29 02:53:46 +08:00
#include <pocketlang.h>
2021-02-08 02:30:29 +08:00
2021-06-07 13:54:06 +08:00
// FIXME: everything below here is temproary and for testing.
2021-05-23 04:59:32 +08:00
2021-05-29 02:53:46 +08:00
void registerModules(PKVM* vm);
2021-05-23 04:59:32 +08:00
2021-05-29 02:53:46 +08:00
// Path public functions (TODO: should I add a header for that?)
2021-06-08 00:56:56 +08:00
void pathInit(void);
2021-05-29 02:53:46 +08:00
bool pathIsAbsolute(const char* path);
void pathGetDirName(const char* path, size_t* length);
size_t pathNormalize(const char* path, char* buff, size_t buff_size);
size_t pathJoin(const char* from, const char* path, char* buffer,
size_t buff_size);
2021-05-23 04:59:32 +08:00
// ---------------------------------------
2021-06-08 00:56:56 +08:00
// FIXME:
typedef struct {
bool repl_mode;
} VmUserData;
2021-06-07 13:54:06 +08:00
void onResultDone(PKVM* vm, PkStringPtr result) {
if ((bool)result.user_data) {
free((void*)result.string);
}
}
void errorFunction(PKVM* vm, PkErrorType type, const char* file, int line,
2021-06-08 00:56:56 +08:00
const char* message) {
VmUserData* ud = (VmUserData*)pkGetUserData(vm);
bool repl = (ud) ? ud->repl_mode : false;
if (type == PK_ERROR_COMPILE) {
2021-06-08 00:56:56 +08:00
if (repl) fprintf(stderr, "Error: %s\n", message);
else fprintf(stderr, "Error: %s\n at \"%s\":%i\n", message, file, line);
2021-06-04 22:55:06 +08:00
} else if (type == PK_ERROR_RUNTIME) {
fprintf(stderr, "Error: %s\n", message);
2021-06-04 22:55:06 +08:00
} else if (type == PK_ERROR_STACKTRACE) {
2021-06-08 00:56:56 +08:00
if (repl) fprintf(stderr, " %s() [line:%i]\n", message, line);
else fprintf(stderr, " %s() [\"%s\":%i]\n", message, file, line);
}
2021-02-08 02:30:29 +08:00
}
2021-05-09 18:28:00 +08:00
void writeFunction(PKVM* vm, const char* text) {
2021-02-12 01:35:43 +08:00
fprintf(stdout, "%s", text);
2021-02-08 02:30:29 +08:00
}
2021-06-08 00:56:56 +08:00
static const char* read_line(void) {
2021-06-07 13:54:06 +08:00
// FIXME: use fgetc char by char till reach a new line.
const int size = 1024;
char* mem = (char*) malloc(size);
fgets(mem, size, stdin);
size_t len = strlen(mem);
2021-06-07 13:54:06 +08:00
// FIXME: handle \r\n, this is temp.
mem[len - 1] = '\0';
return mem;
}
PkStringPtr readFunction(PKVM* vm) {
PkStringPtr result;
result.string = read_line();
result.on_done = onResultDone;
result.user_data = (void*)true;
return result;
2021-02-08 02:30:29 +08:00
}
2021-06-04 22:55:06 +08:00
PkStringPtr resolvePath(PKVM* vm, const char* from, const char* path) {
PkStringPtr result;
2021-05-06 22:19:30 +08:00
result.on_done = onResultDone;
2021-05-29 02:53:46 +08:00
size_t from_dir_len;
pathGetDirName(from, &from_dir_len);
// FIXME: Should handle paths with size of more than FILENAME_MAX.
if (from_dir_len == 0 || pathIsAbsolute(path)) {
size_t length = strlen(path);
char* resolved = (char*)malloc(length + 1);
pathNormalize(path, resolved, length + 1);
result.string = resolved;
result.user_data = (void*)true;
} else {
char from_dir[FILENAME_MAX];
strncpy(from_dir, from, from_dir_len);
from_dir[from_dir_len] = '\0';
char fullpath[FILENAME_MAX];
size_t length = pathJoin(from_dir, path, fullpath, sizeof(fullpath));
char* resolved = (char*)malloc(length + 1);
pathNormalize(fullpath, resolved, length + 1);
result.string = resolved;
result.user_data = (void*)true;
}
2021-05-06 22:19:30 +08:00
return result;
}
2021-06-04 22:55:06 +08:00
PkStringPtr loadScript(PKVM* vm, const char* path) {
2021-06-04 22:55:06 +08:00
PkStringPtr result;
2021-05-06 22:19:30 +08:00
result.on_done = onResultDone;
2021-02-12 01:35:43 +08:00
// Open the file.
FILE* file = fopen(path, "r");
if (file == NULL) {
// Setting .string to NULL to indicate the failure of loading the script.
result.string = NULL;
2021-02-12 01:35:43 +08:00
return result;
}
// Get the source length.
fseek(file, 0, SEEK_END);
long file_size = ftell(file);
fseek(file, 0, SEEK_SET);
// Read source to buffer.
2021-05-19 02:59:09 +08:00
char* buff = (char*)malloc((size_t)(file_size) + 1);
2021-02-12 01:35:43 +08:00
// Using read instead of file_size is because "\r\n" is read as '\n' in
2021-05-22 21:27:40 +08:00
// windows.
size_t read = fread(buff, sizeof(char), file_size, file);
2021-02-12 01:35:43 +08:00
buff[read] = '\0';
fclose(file);
2021-05-06 22:19:30 +08:00
result.string = buff;
result.user_data = (void*)true;
2021-02-12 01:35:43 +08:00
return result;
2021-02-08 02:30:29 +08:00
}
2021-02-12 01:35:43 +08:00
int main(int argc, char** argv) {
2021-02-15 20:49:19 +08:00
const char* notice =
"PocketLang " PK_VERSION_STRING " (https://github.com/ThakeeNathees/pocketlang/)\n"
2021-02-15 20:49:19 +08:00
"Copyright(c) 2020 - 2021 ThakeeNathees.\n"
"Free and open source software under the terms of the MIT license.\n";
2021-06-08 00:56:56 +08:00
const char* usage = "usage: pocket [-c cmd | file]\n";
2021-05-29 02:53:46 +08:00
// TODO: implement arg parse, REPL.
2021-05-22 21:27:40 +08:00
2021-06-08 00:56:56 +08:00
//if (argc < 2) {
// printf("%s\n%s", notice, help);
// return 0;
//}
2021-02-15 20:49:19 +08:00
2021-05-29 02:53:46 +08:00
// Initialize cli.
pathInit();
2021-06-04 22:55:06 +08:00
PkConfiguration config = pkNewConfiguration();
2021-06-07 13:54:06 +08:00
config.error_fn = errorFunction;
2021-02-12 01:35:43 +08:00
config.write_fn = writeFunction;
2021-06-07 13:54:06 +08:00
config.read_fn = readFunction;
2021-02-12 01:35:43 +08:00
config.load_script_fn = loadScript;
2021-05-07 17:41:19 +08:00
config.resolve_path_fn = resolvePath;
2021-02-12 01:35:43 +08:00
2021-06-07 13:54:06 +08:00
PkCompileOptions options = pkNewCompilerOptions();
options.debug = true; // TODO: update this with cli args.
2021-06-04 22:55:06 +08:00
PKVM* vm = pkNewVM(&config);
registerModules(vm);
2021-05-22 21:27:40 +08:00
// FIXME: this is temp till arg parse implemented.
2021-06-08 00:56:56 +08:00
PkResult result;
2021-06-07 13:54:06 +08:00
if (argc == 1) {
2021-06-08 00:56:56 +08:00
PkHandle* module = pkNewModule(vm, "$(REPL)");
options.repl_mode = true;
VmUserData user_data;
user_data.repl_mode = true;
pkSetUserData(vm, &user_data);
printf("%s\n", notice);
bool done = false;
do {
printf(">>> ");
PkStringPtr line = { read_line(), onResultDone, (void*)true };
// TODO: if line is empty continue.
result = pkCompileModule(vm, module, line, &options);
if (result != PK_RESULT_SUCCESS) continue;
PkHandle* main_fn = pkGetFunction(vm, module, PK_IMPLICIT_MAIN_NAME);
PkHandle* fiber = pkNewFiber(vm, main_fn);
result = pkRunFiber(vm, fiber, 0, NULL);
pkReleaseHandle(vm, main_fn);
pkReleaseHandle(vm, fiber);
} while (!done);
pkReleaseHandle(vm, module);
2021-06-07 13:54:06 +08:00
} if (argc >= 3 && strcmp(argv[1], "-c") == 0) {
2021-06-04 22:55:06 +08:00
PkStringPtr source = { argv[2], NULL, NULL };
PkStringPtr path = { "$(Source)", NULL, NULL };
2021-06-07 13:54:06 +08:00
result = pkInterpretSource(vm, source, path, NULL);
2021-05-22 21:27:40 +08:00
pkFreeVM(vm);
return result;
}
2021-06-04 22:55:06 +08:00
PkStringPtr resolved = resolvePath(vm, ".", argv[1]);
PkStringPtr source = loadScript(vm, resolved.string);
2021-05-23 04:59:32 +08:00
2021-06-04 22:55:06 +08:00
if (source.string != NULL) {
2021-06-07 13:54:06 +08:00
result = pkInterpretSource(vm, source, resolved, &options);
2021-05-24 06:17:52 +08:00
2021-06-04 22:55:06 +08:00
} else {
result = PK_RESULT_COMPILE_ERROR;
fprintf(stderr, "Error: cannot open file at \"%s\"\n", resolved.string);
if (resolved.on_done != NULL) resolved.on_done(vm, resolved);
if (source.on_done != NULL) source.on_done(vm, source);
}
pkFreeVM(vm);
2021-05-23 04:59:32 +08:00
return result;
2021-02-07 15:40:00 +08:00
}