Compare commits
3 Commits
b761c1849f
...
8317634e06
Author | SHA1 | Date |
---|---|---|
bog | 8317634e06 | |
bog | 1875adc48d | |
bog | e5b7eea0cf |
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
@ -18,10 +20,13 @@ add_library(moka-core
|
|||
moka.c
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
131
lib/compiler.c
131
lib/compiler.c
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef MK_CONF_H
|
||||
#define MK_CONF_H
|
||||
|
||||
#define MOKA_LIB_DIR "/usr/local/lib/moka"
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef MK_CONF_H
|
||||
#define MK_CONF_H
|
||||
|
||||
#cmakedefine MOKA_LIB_DIR "@MOKA_LIB_DIR@"
|
||||
|
||||
|
||||
#endif
|
25
lib/exec.c
25
lib/exec.c
|
@ -38,25 +38,7 @@ void exec_instr(struct exec* self,
|
|||
switch (instr->opcode)
|
||||
{
|
||||
case OP_CALL: {
|
||||
MOKA fun = moka_pop(moka);
|
||||
struct vec args;
|
||||
vec_init(&args);
|
||||
|
||||
for (ssize_t i=0; i<param; i++)
|
||||
{
|
||||
MOKA arg = moka_pop(moka);
|
||||
vec_push(&args, (void*) arg);
|
||||
}
|
||||
|
||||
struct value* val = moka->global_values.data[
|
||||
moka_get_ref(moka, fun)
|
||||
];
|
||||
|
||||
assert(val->type == TY_NATIVE);
|
||||
struct native* native = val->data.native;
|
||||
(native->fun)(moka, &args);
|
||||
|
||||
vec_free(&args);
|
||||
moka_call(moka, param);
|
||||
self->pc++;
|
||||
} break;
|
||||
|
||||
|
@ -65,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)
|
||||
|
|
23
lib/lexer.c
23
lib/lexer.c
|
@ -33,6 +33,11 @@ struct token* lexer_try_new_next(struct lexer* self)
|
|||
assert(self);
|
||||
struct token* tok = NULL;
|
||||
|
||||
if (!status_is_ok(self->status) > 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lexer_skip_spaces(self);
|
||||
|
||||
if ( (tok=lexer_try_new_text(self, TOKEN_OPAR, "(")) )
|
||||
|
@ -348,12 +353,17 @@ struct token* lexer_try_new_ident(struct lexer* self)
|
|||
cursor++;
|
||||
}
|
||||
|
||||
struct token* tok = malloc(sizeof(struct token));
|
||||
token_init(tok, TOKEN_IDENT, value.value);
|
||||
str_free(&value);
|
||||
if (value.size > 0)
|
||||
{
|
||||
struct token* tok = malloc(sizeof(struct token));
|
||||
token_init(tok, TOKEN_IDENT, value.value);
|
||||
str_free(&value);
|
||||
|
||||
self->context.cursor = cursor;
|
||||
return tok;
|
||||
self->context.cursor = cursor;
|
||||
return tok;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool lexer_is_sep(struct lexer* self, size_t index)
|
||||
|
@ -391,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,
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
#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)
|
||||
{
|
||||
assert(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)
|
||||
{
|
||||
assert(self);
|
||||
assert(path);
|
||||
|
||||
struct str source;
|
||||
str_init(&source);
|
||||
|
||||
FILE* file = fopen(path, "r+");
|
||||
size_t sz;
|
||||
char buf;
|
||||
|
||||
while ( (sz=fread(&buf, sizeof(char), 1, file)) )
|
||||
{
|
||||
str_push(&source, buf);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
int ret = module_load_from_str(self, source.value);
|
||||
|
||||
str_free(&source);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int module_load_from_str(struct module* self, char const* source)
|
||||
{
|
||||
status_init(&self->status);
|
||||
|
||||
struct lexer lex;
|
||||
lexer_init(&lex, source, &self->status);
|
||||
|
||||
struct parser parser;
|
||||
parser_init(&parser, &lex);
|
||||
struct node* root = parser_try_new_root(&parser);
|
||||
|
||||
if (!root || !status_is_ok(&self->status))
|
||||
{
|
||||
status_dump(&self->status);
|
||||
goto free_parser;
|
||||
}
|
||||
|
||||
struct compiler compiler;
|
||||
compiler_init(&compiler, &self->status);
|
||||
|
||||
compiler_compile(&compiler, root, &self->prog, &self->moka);
|
||||
|
||||
if (!status_is_ok(&self->status))
|
||||
{
|
||||
status_dump(&self->status);
|
||||
goto free_compiler;
|
||||
}
|
||||
|
||||
struct exec exec;
|
||||
exec_init(&exec);
|
||||
|
||||
exec_prog(&exec, &self->moka, &self->prog);
|
||||
exec_free(&exec);
|
||||
|
||||
free_compiler:
|
||||
compiler_free(&compiler);
|
||||
node_free(root);
|
||||
free(root);
|
||||
free_parser:
|
||||
parser_free(&parser);
|
||||
lexer_free(&lex);
|
||||
|
||||
int ret = status_is_ok(&self->status) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef MK_MODULE_H
|
||||
#define MK_MODULE_H
|
||||
|
||||
#include "commons.h"
|
||||
#include "status.h"
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
#include "prog.h"
|
||||
#include "moka.h"
|
||||
#include "compiler.h"
|
||||
#include "exec.h"
|
||||
#include "status.h"
|
||||
|
||||
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);
|
||||
|
||||
#endif
|
179
lib/moka.c
179
lib/moka.c
|
@ -3,6 +3,23 @@
|
|||
#include "compiler.h"
|
||||
#include "prog.h"
|
||||
#include "exec.h"
|
||||
#include "module.h"
|
||||
|
||||
void moka_mod_init(struct moka_mod* self,
|
||||
char const* name,
|
||||
struct module* new_module)
|
||||
{
|
||||
assert(self);
|
||||
self->name = strdup(name);
|
||||
self->module = new_module;
|
||||
}
|
||||
|
||||
void moka_mod_free(struct moka_mod* self)
|
||||
{
|
||||
free(self->name);
|
||||
module_free(self->module);
|
||||
free(self->module);
|
||||
}
|
||||
|
||||
void moka_init(struct moka* self, struct status* status)
|
||||
{
|
||||
|
@ -11,6 +28,7 @@ void moka_init(struct moka* self, struct status* status)
|
|||
symtable_init(&self->symtable);
|
||||
vec_init(&self->frame_stack);
|
||||
vec_init(&self->global_values);
|
||||
vec_init(&self->modules);
|
||||
|
||||
struct frame* frame = malloc(sizeof(struct frame));
|
||||
frame_init(frame);
|
||||
|
@ -37,10 +55,130 @@ void moka_free(struct moka* self)
|
|||
symtable_free(&self->symtable);
|
||||
vec_free_elements(&self->frame_stack, (void*) frame_free);
|
||||
vec_free(&self->frame_stack);
|
||||
|
||||
vec_free_elements(&self->modules, (void*) moka_mod_free);
|
||||
vec_free(&self->modules);
|
||||
|
||||
vec_free_elements(&self->global_values, (void*) value_free);
|
||||
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)
|
||||
|
@ -149,6 +287,38 @@ void moka_dump(struct moka* self, MOKA value)
|
|||
abort();
|
||||
}
|
||||
|
||||
MOKA moka_call(struct moka* self, int arg_count)
|
||||
{
|
||||
MOKA fun = moka_pop(self);
|
||||
struct vec args;
|
||||
vec_init(&args);
|
||||
|
||||
for (ssize_t i=0; i<arg_count; i++)
|
||||
{
|
||||
MOKA arg = moka_pop(self);
|
||||
vec_push(&args, (void*) arg);
|
||||
}
|
||||
|
||||
struct value* val = self->global_values.data[
|
||||
moka_get_ref(self, fun)
|
||||
];
|
||||
|
||||
assert(val->type == TY_NATIVE);
|
||||
struct native* native = val->data.native;
|
||||
(native->fun)(self, &args);
|
||||
|
||||
vec_free(&args);
|
||||
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);
|
||||
|
@ -271,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;
|
||||
}
|
||||
|
@ -305,6 +476,12 @@ native_fun_t moka_get_native(struct moka* self, MOKA value)
|
|||
MOKA moka_eval_lazy(struct moka* self, MOKA lazy_value)
|
||||
{
|
||||
assert(self);
|
||||
|
||||
if (!moka_is(self, lazy_value, TY_LAZY))
|
||||
{
|
||||
return lazy_value;
|
||||
}
|
||||
|
||||
struct node* node = moka_get_lazy(self, lazy_value);
|
||||
|
||||
struct compiler compiler;
|
||||
|
@ -317,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))
|
||||
{
|
||||
|
|
28
lib/moka.h
28
lib/moka.h
|
@ -12,19 +12,43 @@ struct frame
|
|||
struct vec local_values;
|
||||
};
|
||||
|
||||
struct moka_mod
|
||||
{
|
||||
char* name;
|
||||
struct module* module;
|
||||
};
|
||||
|
||||
struct moka
|
||||
{
|
||||
struct status* status;
|
||||
struct symtable symtable;
|
||||
struct vec frame_stack;
|
||||
struct vec global_values;
|
||||
struct vec modules;
|
||||
};
|
||||
|
||||
void moka_mod_init(struct moka_mod* self,
|
||||
char const* name,
|
||||
struct module* new_module);
|
||||
void moka_mod_free(struct moka_mod* self);
|
||||
|
||||
void moka_init(struct moka* self, struct status* status);
|
||||
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);
|
||||
|
@ -39,6 +63,10 @@ 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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
77
src/main.c
77
src/main.c
|
@ -5,85 +5,18 @@
|
|||
#include <compiler.h>
|
||||
#include <exec.h>
|
||||
#include <moka.h>
|
||||
#include <module.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc <= 1) { return EXIT_FAILURE; }
|
||||
|
||||
struct status status;
|
||||
status_init(&status);
|
||||
struct module module;
|
||||
module_init(&module);
|
||||
|
||||
struct str source;
|
||||
str_init(&source);
|
||||
int ret = module_load_from_file(&module, argv[1]);
|
||||
|
||||
{
|
||||
FILE* file = fopen(argv[1], "r+");
|
||||
size_t sz;
|
||||
char buf;
|
||||
while ( (sz=fread(&buf, sizeof(char), 1, file)) )
|
||||
{
|
||||
str_push(&source, buf);
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
struct lexer lex;
|
||||
lexer_init(&lex, source.value, &status);
|
||||
|
||||
struct parser parser;
|
||||
parser_init(&parser, &lex);
|
||||
struct node* root = parser_try_new_root(&parser);
|
||||
|
||||
if (!root || !status_is_ok(&status))
|
||||
{
|
||||
status_dump(&status);
|
||||
goto free_parser;
|
||||
}
|
||||
|
||||
struct compiler compiler;
|
||||
compiler_init(&compiler, &status);
|
||||
|
||||
struct prog prog;
|
||||
prog_init(&prog);
|
||||
|
||||
|
||||
struct moka moka;
|
||||
moka_init(&moka, &status);
|
||||
compiler_compile(&compiler, root, &prog, &moka.symtable);
|
||||
|
||||
if (!status_is_ok(&status))
|
||||
{
|
||||
status_dump(&status);
|
||||
goto free_moka;
|
||||
}
|
||||
|
||||
|
||||
struct exec exec;
|
||||
exec_init(&exec);
|
||||
|
||||
exec_prog(&exec, &moka, &prog);
|
||||
|
||||
if (moka_has_top(&moka))
|
||||
{
|
||||
MOKA value = moka_top(&moka);
|
||||
moka_dump(&moka, value);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
exec_free(&exec);
|
||||
free_moka:
|
||||
moka_free(&moka);
|
||||
prog_free(&prog);
|
||||
compiler_free(&compiler);
|
||||
node_free(root);
|
||||
free(root);
|
||||
free_parser:
|
||||
parser_free(&parser);
|
||||
lexer_free(&lex);
|
||||
str_free(&source);
|
||||
|
||||
int ret = status_is_ok(&status) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
status_free(&status);
|
||||
module_free(&module);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
@ -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);
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue