import modules.

main
bog 2024-03-29 05:46:35 +01:00
parent 1875adc48d
commit 8317634e06
22 changed files with 683 additions and 26 deletions

View File

@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.29)
project(moka)
set(MOKA_LIB_DIR ${CMAKE_INSTALL_PREFIX}/lib/moka)
set(CMAKE_EXPORT_COMPILE_COMMANDS On)
add_subdirectory(lib)
add_subdirectory(src)
add_subdirectory(tests)
add_subdirectory(std)
set_property(TARGET moka-core PROPERTY C_STANDARD 99)
set_property(TARGET moka-tests PROPERTY C_STANDARD 99)

View File

@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.29)
project(moka-lib)
configure_file(conf.in.h ${CMAKE_SOURCE_DIR}/lib/conf.h)
add_library(moka-core
commons.c
status.c
@ -19,10 +21,12 @@ add_library(moka-core
symtable.c
native.c
module.c
path.c
builtins.c
)
target_compile_options(moka-core
PUBLIC -Wall -Wextra -g
PUBLIC -Wall -Wextra -g -fPIC
)
target_include_directories(moka-core

48
lib/builtins.c Normal file
View File

@ -0,0 +1,48 @@
#include "builtins.h"
#include "node.h"
#include "path.h"
#include "module.h"
void register_builtins(struct moka* moka)
{
assert(moka);
moka_decl_native(moka, "println", mk_println);
moka_decl_native(moka, "define", mk_define);
}
MOKA mk_println(struct moka* moka, struct vec* args)
{
assert(moka);
assert(args);
int line = 0;
for (size_t i=0; i<args->size; i++)
{
MOKA val = (MOKA) args->data[i];
if (i > 0) { printf(" "); }
else { line = moka_get_lazy(moka, val)->line; }
moka_dump(moka, MK_EVAL(val));
}
printf("\n");
return moka_push_int(moka, 0, line);
}
MOKA mk_define(struct moka* moka, struct vec* args)
{
assert(moka); assert(args);
assert(args->size == 2);
MOKA target = (MOKA) args->data[0];
MOKA value = MK_EVAL((MOKA) args->data[1]);
struct node* node = moka_get_lazy(moka, target);
moka_push(moka, value);
assert(node->token);
moka_decl_var(moka, node->token->value, value);
return value;
}

14
lib/builtins.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef MK_BUILTINS_H
#define MK_BUILTINS_H
#include "commons.h"
#include "moka.h"
#define MK_EVAL(X) moka_eval_lazy(moka, X)
void register_builtins(struct moka* moka);
MOKA mk_println(struct moka* moka, struct vec* args);
MOKA mk_define(struct moka* moka, struct vec* args);
#endif

View File

@ -1,4 +1,8 @@
#include "compiler.h"
#include "moka.h"
#include "module.h"
#include "path.h"
#include "conf.h"
void compiler_init(struct compiler* self,
struct status* status)
@ -15,7 +19,7 @@ void compiler_free(struct compiler* self)
void compiler_compile(struct compiler* self,
struct node* node,
struct prog* prog,
struct symtable* symtable)
struct moka* moka)
{
assert(self); assert(node); assert(prog);
@ -25,26 +29,30 @@ void compiler_compile(struct compiler* self,
for (size_t i=0; i<node->children.size; i++)
{
struct node* child = node->children.data[i];
compiler_compile(self, child, prog, symtable);
compiler_compile(self, child, prog, moka);
}
} break;
case NODE_CALL: {
struct node* child = node->children.data[0];
if (strcmp(child->token->value, "import") == 0)
{
compiler_import(self, moka, node);
break;
}
for (size_t i=0; i<node->children.size - 1; i++)
{
// size_t k = node->children.size - 1 - i;
// struct node* child = node->children.data[k];
// compiler_compile(self, child, prog, symtable);
size_t k = node->children.size - 1 - i;
struct node* child = node->children.data[k];
struct node* mychild = node->children.data[k];
struct value* val = malloc(sizeof(struct value));
value_init_lazy(val, child, child->line);
value_init_lazy(val, mychild, mychild->line);
size_t addr = prog_add_new_value(prog, val);
prog_add_instruction(prog, OP_PUSH, addr);
}
struct node* child = node->children.data[0];
compiler_compile(self, child, prog, symtable);
compiler_compile(self, child, prog, moka);
prog_add_instruction(prog, OP_CALL, node->children.size - 1);
} break;
@ -88,8 +96,10 @@ void compiler_compile(struct compiler* self,
} break;
case NODE_IDENT: {
struct entry const* entry = symtable_try_get(symtable,
node->token->value);
struct entry const* entry = symtable_try_get(
&moka->symtable,
node->token->value
);
if (!entry)
{
status_push(self->status, STATUS_ERROR, node->line,
@ -114,3 +124,102 @@ void compiler_compile(struct compiler* self,
} break;
}
}
void compiler_import(struct compiler* self,
struct moka* moka,
struct node* node)
{
(void) self;
struct node* arg = node->children.data[1];
char const* name = arg->token->value;
if (!path_is_local(name))
{
char source_name_loc[MK_STRLEN];
path_apply_ext(name, "mk", source_name_loc, MK_STRLEN);
char source_name[MK_STRLEN*2];
snprintf(source_name, MK_STRLEN * 2, "%s/%s",
MOKA_LIB_DIR,
source_name_loc);
char lib_name_tmp[MK_STRLEN];
path_apply_ext(name, "so", lib_name_tmp, MK_STRLEN);
char lib_name_loc[MK_STRLEN];
path_apply_prefix(lib_name_tmp, "lib", lib_name_loc, MK_STRLEN);
char lib_name[MK_STRLEN * 2];
snprintf(lib_name, MK_STRLEN * 2, "%s/%s",
MOKA_LIB_DIR,
lib_name_loc);
if (path_exists(source_name))
{
struct module* module = malloc(sizeof(struct module));
module_init(module);
module_load_from_file(module, source_name);
char modname[MK_STRLEN];
path_get_mod_name(name, modname, MK_STRLEN);
moka_import_module(moka, modname, module);
return;
}
else if (path_exists(lib_name))
{
struct module* module = malloc(sizeof(struct module));
module_init(module);
module_load_from_dl(module, lib_name);
char modname[MK_STRLEN];
path_get_mod_name(name, modname, MK_STRLEN);
moka_import_module(moka, modname, module);
return;
}
}
else
{
char source_name[MK_STRLEN];
path_apply_ext(name, "mk", source_name, MK_STRLEN);
char lib_name_tmp[MK_STRLEN];
path_apply_ext(name, "so", lib_name_tmp, MK_STRLEN);
char lib_name[MK_STRLEN];
path_apply_prefix(lib_name_tmp, "lib", lib_name, MK_STRLEN);
if (path_exists(source_name))
{
struct module* module = malloc(sizeof(struct module));
module_init(module);
module_load_from_file(module, source_name);
char modname[MK_STRLEN];
path_get_mod_name(name, modname, MK_STRLEN);
moka_import_module(moka, modname, module);
return;
}
else if (path_exists(lib_name))
{
struct module* module = malloc(sizeof(struct module));
module_init(module);
module_load_from_dl(module, lib_name);
char modname[MK_STRLEN];
path_get_mod_name(name, modname, MK_STRLEN);
moka_import_module(moka, modname, module);
return;
}
}
status_push(
self->status,
STATUS_ERROR,
node->line,
"unknown module <%s>",
name
);
}

View File

@ -19,5 +19,10 @@ void compiler_free(struct compiler* self);
void compiler_compile(struct compiler* self,
struct node* node,
struct prog* prog,
struct symtable* symtable);
struct moka* moka);
void compiler_import(struct compiler* self,
struct moka* moka,
struct node* node);
#endif

7
lib/conf.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef MK_CONF_H
#define MK_CONF_H
#define MOKA_LIB_DIR "/usr/local/lib/moka"
#endif

7
lib/conf.in.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef MK_CONF_H
#define MK_CONF_H
#cmakedefine MOKA_LIB_DIR "@MOKA_LIB_DIR@"
#endif

View File

@ -47,6 +47,11 @@ void exec_instr(struct exec* self,
self->pc++;
} break;
case OP_LOCAL_LOAD: {
moka_push(moka, param);
self->pc++;
} break;
case OP_PUSH: {
struct value* value = prog->values.data[param];
switch (value->type)

View File

@ -401,7 +401,8 @@ bool lexer_is_ident(struct lexer* self, size_t index)
|| c == '_'
|| c == '!'
|| c == '?'
|| c == '-';
|| c == '-'
|| c == ':';
}
struct token* lexer_try_new_text(struct lexer* self,

View File

@ -1,11 +1,16 @@
#include <dlfcn.h>
#include "module.h"
#include "builtins.h"
void module_init(struct module* self)
{
assert(self);
status_init(&self->status);
moka_init(&self->moka, &self->status);
register_builtins(&self->moka);
prog_init(&self->prog);
self->handle = NULL;
}
void module_free(struct module* self)
@ -14,6 +19,36 @@ void module_free(struct module* self)
status_free(&self->status);
prog_free(&self->prog);
moka_free(&self->moka);
if (self->handle)
{
dlclose(self->handle);
self->handle = NULL;
}
}
int module_load_from_dl(struct module* self, char const* path)
{
assert(self);
assert(path);
self->handle = dlopen(path, RTLD_NOW);
if (!self->handle)
{
return EXIT_FAILURE;
}
void (*f)(struct module*) = dlsym(self->handle, "create_module");
if (!f)
{
dlclose(self->handle);
return EXIT_FAILURE;
}
f(self);
return EXIT_SUCCESS;
}
int module_load_from_file(struct module* self, char const* path)
@ -62,7 +97,7 @@ int module_load_from_str(struct module* self, char const* source)
struct compiler compiler;
compiler_init(&compiler, &self->status);
compiler_compile(&compiler, root, &self->prog, &self->moka.symtable);
compiler_compile(&compiler, root, &self->prog, &self->moka);
if (!status_is_ok(&self->status))
{

View File

@ -16,11 +16,13 @@ struct module
struct status status;
struct prog prog;
struct moka moka;
void* handle;
};
void module_init(struct module* self);
void module_free(struct module* self);
int module_load_from_dl(struct module* self, char const* path);
int module_load_from_file(struct module* self, char const* path);
int module_load_from_str(struct module* self, char const* source);

View File

@ -6,7 +6,7 @@
#include "module.h"
void moka_mod_init(struct moka_mod* self,
char* name,
char const* name,
struct module* new_module)
{
assert(self);
@ -63,6 +63,122 @@ void moka_free(struct moka* self)
vec_free(&self->global_values);
}
void moka_import_module(struct moka* self,
char const* name,
struct module* module)
{
assert(self);
assert(name);
assert(module);
struct symtable* mod_sym = &module->moka.symtable;
struct env* env = mod_sym->root;
struct frame* mod_frame = moka_frame(&module->moka);
struct vec* mod_locals = &mod_frame->local_values;
struct vec* mod_globals = &module->moka.global_values;
for (size_t i=0; i<env->entries.size; i++)
{
struct entry const* entry = env->entries.data[i];
struct value* val;
if (entry->is_local)
{
val = mod_locals->data[entry->addr];
}
else
{
val = mod_globals->data[entry->addr];
}
char new_name[MK_STRLEN];
snprintf(new_name, MK_STRLEN, "%s::%s", name, entry->name);
switch (val->type)
{
case TY_NATIVE: {
moka_decl_native(
self,
new_name,
val->data.native->fun
);
} break;
case TY_SYMBOL: {
moka_decl_var(
self,
new_name,
moka_push_symbol(self,
val->data.sym,
val->line));
} break;
case TY_STRING: {
moka_decl_var(
self,
new_name,
moka_push_string(self,
val->data.str,
val->line));
} break;
case TY_INT: {
moka_decl_var(
self,
new_name,
moka_push_int(self,
val->data.integer,
val->line));
} break;
default: {
fprintf(stderr,
"cannot import value of type <%s>\n",
TypeKindStr[val->type]);
abort();
} break;
}
}
struct moka_mod* mod = malloc(sizeof(struct moka_mod));
moka_mod_init(mod, (char*) name, module);
vec_push(&self->modules, mod);
}
struct module* moka_try_get_module(struct moka* self,
char const* name)
{
assert(self);
assert(name);
for (size_t i=0; i<self->modules.size; i++)
{
struct moka_mod* mod = self->modules.data[i];
if (strcmp(name, mod->name))
{
return mod->module;
}
}
return NULL;
}
void moka_decl_var(struct moka* self,
char* name,
MOKA value)
{
assert(self); assert(name);
symtable_declare(
&self->symtable,
name,
value,
true);
}
void moka_decl_native(struct moka* self,
char* name,
native_fun_t fun)
@ -195,6 +311,14 @@ MOKA moka_call(struct moka* self, int arg_count)
return moka_top(self);
}
MOKA moka_push(struct moka* self, MOKA value)
{
assert(self);
struct frame* frame = moka_frame(self);
vec_push(&frame->stack, (void*) value);
return value;
}
MOKA moka_push_int(struct moka* self, int value, int line)
{
assert(self);
@ -317,6 +441,7 @@ size_t moka_get_ref(struct moka* self, MOKA value)
assert(self);
struct frame* frame = moka_frame(self);
struct value* val = frame->local_values.data[value];
assert(val->type == TY_REF);
return val->data.ref;
}
@ -369,7 +494,7 @@ MOKA moka_eval_lazy(struct moka* self, MOKA lazy_value)
&compiler,
node,
&prog,
&self->symtable);
self);
if (!status_is_ok(self->status))
{

View File

@ -27,8 +27,8 @@ struct moka
struct vec modules;
};
void moka_mod_init(struct moka_mod* self,
char* name,
void moka_mod_init(struct moka_mod* self,
char const* name,
struct module* new_module);
void moka_mod_free(struct moka_mod* self);
@ -37,6 +37,18 @@ void frame_init(struct frame* self);
void frame_free(struct frame* self);
void moka_free(struct moka* self);
void moka_import_module(struct moka* self,
char const* name,
struct module* module);
struct module* moka_try_get_module(struct moka* self,
char const* name);
void moka_decl_var(struct moka* self,
char* name,
MOKA value);
void moka_decl_native(struct moka* self,
char* name,
native_fun_t fun);
@ -52,6 +64,9 @@ TypeKind moka_type_of(struct moka* self, MOKA value);
void moka_dump(struct moka* self, MOKA value);
MOKA moka_call(struct moka* self, int arg_count);
MOKA moka_push(struct moka* self, MOKA value);
MOKA moka_push_int(struct moka* self, int value, int line);
int moka_get_int(struct moka* self, MOKA value);

162
lib/path.c Normal file
View File

@ -0,0 +1,162 @@
#include "path.h"
bool path_is_local(char const* path)
{
assert(path);
if (strlen(path) == 0)
{
return false;
}
return path[0] == '.';
}
void path_get_mod_name(char const* path, char* buffer, size_t size)
{
assert(path);
size_t sz = strlen(path);
size_t k = 0;
for (size_t i=0; i<sz; i++)
{
if (path[i] == '.' && i != 0)
{
bool at_end = true;
for (size_t j=i+1; j<sz; j++)
{
if (path[j] == '/' && j != sz - 1)
{
at_end = false;
break;
}
}
if (at_end)
{
break;
}
}
if (path[i] == '/' && i != sz - 1)
{
k = 0;
}
else if (path[i] != '/')
{
if (k >= size) { break; }
buffer[k] = path[i];
k++;
}
}
buffer[k] = '\0';
}
bool path_exists(char const* path)
{
FILE* file;
if ( (file = fopen(path, "r")) )
{
fclose(file);
return true;
}
return false;
}
size_t path_apply_ext(char const* path,
char const* ext,
char* buffer,
size_t size)
{
assert(path); assert(ext); assert(buffer);
ssize_t ext_idx = -1;
size_t path_sz = strlen(path);
char result[MK_STRLEN];
memset(result, '\0', sizeof(char) * MK_STRLEN);
memcpy(result, path, sizeof(char) * path_sz);
for (size_t i=0; i<path_sz; i++)
{
size_t k = path_sz - i - 1;
if (path[k] == '.')
{
ext_idx = k;
break;
}
}
if (ext_idx == -1 || (path[0] == '.' && ext_idx == 0))
{
return snprintf(buffer, size, "%s.%s", path, ext);
}
size_t sz = 0;
for (ssize_t i=0; i<=ext_idx; i++)
{
sz += snprintf(buffer + sz, size - sz, "%c", path[i]);
}
size_t const ext_sz = strlen(ext);
for (size_t i=0; i<ext_sz; i++)
{
sz += snprintf(buffer + sz, size - sz, "%c", ext[i]);
}
return sz;
}
size_t path_apply_prefix(char const* path,
char const* prefix,
char* buffer,
size_t size)
{
assert(path); assert(prefix); assert(buffer);
size_t const path_sz = strlen(path);
char const* itr = strstr(path, prefix);
size_t prefix_pos = 0;
for (size_t i=0; i<path_sz; i++)
{
size_t k = path_sz - 1 - i;
if (path[k] == '/')
{
prefix_pos = k + 1;
break;
}
}
if (path + prefix_pos == itr)
{
memcpy(buffer, path, sizeof(char) * path_sz);
return 0;
}
size_t sz = 0;
for (size_t i=0; i<prefix_pos; i++)
{
sz += snprintf(buffer + sz, size - sz, "%c", path[i]);
}
size_t const prefix_sz = strlen(prefix);
for (size_t i=0; i<prefix_sz; i++)
{
sz += snprintf(buffer + sz, size - sz, "%c", prefix[i]);
}
for (size_t i=prefix_pos; i<path_sz; i++)
{
sz += snprintf(buffer + sz, size - sz, "%c", path[i]);
}
return sz;
}

19
lib/path.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef MK_PATH_H
#define MK_PATH_H
#include "commons.h"
bool path_is_local(char const* path);
void path_get_mod_name(char const* path, char* buffer, size_t size);
bool path_exists(char const* path);
size_t path_apply_ext(char const* path,
char const* ext,
char* buffer,
size_t size);
size_t path_apply_prefix(char const* path,
char const* prefix,
char* buffer,
size_t size);
#endif

View File

@ -35,6 +35,7 @@ size_t prog_add_instruction(struct prog* self,
{
case OP_CALL:
case OP_GLOBAL_LOAD:
case OP_LOCAL_LOAD:
case OP_PUSH: {
self->stack_addr++;
} break;

View File

@ -38,6 +38,7 @@ void value_init_symbol(struct value* self, char const* value, int line);
void value_init_ref(struct value* self, size_t value, int line);
void value_init_native(struct value* self, struct native* value, int line);
void value_init_lazy(struct value* self, struct node* value, int line);
void value_free(struct value* self);
#endif

View File

@ -16,13 +16,6 @@ int main(int argc, char** argv)
int ret = module_load_from_file(&module, argv[1]);
if (moka_has_top(&module.moka))
{
MOKA value = moka_top(&module.moka);
moka_dump(&module.moka, value);
printf("\n");
}
module_free(&module);
return ret;

18
std/CMakeLists.txt Normal file
View File

@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.29)
project(moka-std)
function(add_stdlib target source)
add_library(${target} SHARED
${source}
)
add_dependencies(${target} moka-core)
target_link_libraries(${target}
PUBLIC moka-core
)
install(TARGETS ${target} LIBRARY DESTINATION lib/moka)
endfunction()

View File

@ -2,6 +2,7 @@
#include <check.h>
#include "lexer.h"
#include "parser.h"
#include "path.h"
int main()
{
@ -9,6 +10,7 @@ int main()
register_lexer(s);
register_parser(s);
register_path(s);
SRunner* runner = srunner_create(s);
srunner_run_all(runner, CK_VERBOSE);

82
tests/path.h Normal file
View File

@ -0,0 +1,82 @@
#ifndef MK_TEST_PATH_H
#define MK_TEST_PATH_H
#include <check.h>
#include <path.h>
START_TEST(path_check_local)
{
ck_assert(true == path_is_local("./hello/world"));
ck_assert(true == path_is_local("../hello/world"));
ck_assert(false == path_is_local(""));
ck_assert(false == path_is_local("hello/world"));
ck_assert(false == path_is_local("/hello/world"));
ck_assert(false == path_is_local("/../hello/world"));
}
END_TEST
START_TEST(test_path_get_mod_name)
{
size_t const SZ = MK_STRLEN;
char buffer[SZ];
path_get_mod_name("./hello/world", buffer, SZ);
ck_assert_str_eq("world", buffer);
path_get_mod_name("./hello/world/", buffer, SZ);
ck_assert_str_eq("world", buffer);
path_get_mod_name("./hello.ext", buffer, SZ);
ck_assert_str_eq("hello", buffer);
path_get_mod_name("./hello.ext/world.com", buffer, SZ);
ck_assert_str_eq("world", buffer);
path_get_mod_name("./hello.ext/world.com/", buffer, SZ);
ck_assert_str_eq("world", buffer);
}
END_TEST
START_TEST(test_path_apply_ext)
{
size_t const SZ = MK_STRLEN;
char buffer[SZ];
path_apply_ext("./hello/world", "abc", buffer, SZ);
ck_assert_str_eq("./hello/world.abc", buffer);
path_apply_ext("world.mk", "abc", buffer, SZ);
ck_assert_str_eq("world.abc", buffer);
path_apply_ext("./hello/world.xy", "xy", buffer, SZ);
ck_assert_str_eq("./hello/world.xy", buffer);
}
END_TEST
START_TEST(test_path_apply_prefix)
{
size_t const SZ = MK_STRLEN;
char buffer[SZ];
path_apply_prefix("./hello/world.so", "lib", buffer, SZ);
ck_assert_str_eq("./hello/libworld.so", buffer);
path_apply_prefix("./hello/libworld.so", "lib", buffer, SZ);
ck_assert_str_eq("./hello/libworld.so", buffer);
path_apply_prefix("./hello/alibworld.so", "lib", buffer, SZ);
ck_assert_str_eq("./hello/libalibworld.so", buffer);
}
END_TEST
void register_path(Suite* suite)
{
TCase* tcase = tcase_create("path");
suite_add_tcase(suite, tcase);
tcase_add_test(tcase, path_check_local);
tcase_add_test(tcase, test_path_get_mod_name);
tcase_add_test(tcase, test_path_apply_ext);
tcase_add_test(tcase, test_path_apply_prefix);
}
#endif