From 7847fe4c5dd98fa94ea47e4988e6cfbf9af4c28a Mon Sep 17 00:00:00 2001 From: bog Date: Fri, 29 Mar 2024 21:11:46 +0100 Subject: [PATCH] :sparkles: arrays. --- doc/grammar.bnf | 2 ++ lib/CMakeLists.txt | 1 + lib/array.c | 22 ++++++++++++ lib/array.h | 20 +++++++++++ lib/builtins.c | 18 ++++++++++ lib/builtins.h | 1 + lib/compiler.c | 45 +++++++++++++++++++++++ lib/compiler.h | 13 ++++--- lib/exec.c | 5 +++ lib/lexer.c | 12 +++++++ lib/moka.c | 79 +++++++++++++++++++++++++++++++++------- lib/moka.h | 1 + lib/parser.c | 56 ++++++++++++++++++++++++++--- lib/parser.h | 1 + lib/prog.c | 12 ++++--- lib/prog.h | 3 +- lib/token.h | 2 +- lib/value.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++ lib/value.h | 7 +++- tests/lexer.h | 11 ++++++ tests/parser.h | 11 ++++++ 21 files changed, 384 insertions(+), 27 deletions(-) create mode 100644 lib/array.c create mode 100644 lib/array.h diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 8281f27..0688110 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -2,6 +2,8 @@ ROOT ::= ATOM* EXPR ::= | ATOM | CALL +| ARRAY +ARRAY ::= osquare EXPR* csquare CALL ::= opar EXPR EXPR* cpar ATOM ::= | int diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 2be8495..2b0476e 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -23,6 +23,7 @@ add_library(moka-core module.c path.c builtins.c + array.c ) target_compile_options(moka-core diff --git a/lib/array.c b/lib/array.c new file mode 100644 index 0000000..99bb996 --- /dev/null +++ b/lib/array.c @@ -0,0 +1,22 @@ +#include "array.h" +#include "value.h" + +void array_init(struct array* self) +{ + assert(self); + vec_init(&self->values); +} + +void array_free(struct array* self) +{ + assert(self); + vec_free(&self->values); +} + +void array_push_value(struct array* self, MOKA value) +{ + assert(self); + assert(value); + + vec_push(&self->values, (void*) value); +} diff --git a/lib/array.h b/lib/array.h new file mode 100644 index 0000000..60d4523 --- /dev/null +++ b/lib/array.h @@ -0,0 +1,20 @@ +#ifndef MK_ARRAY_H +#define MK_ARRAY_H + +#include "commons.h" +#include "vec.h" + +struct value; + +struct array +{ + struct vec values; +}; + +void array_init(struct array* self); +void array_free(struct array* self); + +void array_push_value(struct array* self, MOKA value); + +#endif + diff --git a/lib/builtins.c b/lib/builtins.c index 2476399..f4ef24c 100644 --- a/lib/builtins.c +++ b/lib/builtins.c @@ -8,6 +8,7 @@ void register_builtins(struct moka* moka) assert(moka); moka_decl_native(moka, "println", mk_println); moka_decl_native(moka, "define", mk_define); + moka_decl_native(moka, "array", mk_array); } MOKA mk_println(struct moka* moka, struct vec* args) @@ -46,3 +47,20 @@ MOKA mk_define(struct moka* moka, struct vec* args) return value; } + +MOKA mk_array(struct moka* moka, struct vec* args) +{ + assert(moka); + assert(args); + + for (size_t i=0; isize; i++) + { + MOKA arg = (MOKA) args->data[i]; + MOKA val = MK_EVAL(arg); + moka_push(moka, val); + } + + moka_make_array(moka, args->size); + + return moka_top(moka); +} diff --git a/lib/builtins.h b/lib/builtins.h index c0cb800..354345b 100644 --- a/lib/builtins.h +++ b/lib/builtins.h @@ -10,5 +10,6 @@ void register_builtins(struct moka* moka); MOKA mk_println(struct moka* moka, struct vec* args); MOKA mk_define(struct moka* moka, struct vec* args); +MOKA mk_array(struct moka* moka, struct vec* args); #endif diff --git a/lib/compiler.c b/lib/compiler.c index a9f8f56..59a2e60 100644 --- a/lib/compiler.c +++ b/lib/compiler.c @@ -36,6 +36,14 @@ void compiler_compile(struct compiler* self, case NODE_CALL: { struct node* child = node->children.data[0]; + if (strcmp(child->token->value, "quote") == 0) + { + assert(node->children.size == 2); + struct node* arg = node->children.data[1]; + compiler_quote(self, arg, prog, moka); + break; + } + if (strcmp(child->token->value, "import") == 0) { compiler_import(self, moka, node); @@ -223,3 +231,40 @@ void compiler_import(struct compiler* self, name ); } + +void compiler_quote(struct compiler* self, + struct node* node, + struct prog* prog, + struct moka* moka) +{ + assert(self); + assert(node); + assert(prog); + assert(moka); + + switch (node->kind) + { + case NODE_INT: { + compiler_compile(self, node, prog, moka); + } break; + case NODE_IDENT: { + node->kind = NODE_SYMBOL; + compiler_compile(self, node, prog, moka); + } break; + case NODE_CALL: { + for (size_t i=0; ichildren.size; i++) + { + compiler_quote(self, node->children.data[i], + prog, moka); + } + + prog_add_instruction(prog, OP_MAKE_ARRAY, + node->children.size); + } break; + default: { + fprintf(stderr, "cannot quote node <%s>\n", + NodeKindStr[node->kind]); + abort(); + } break; + } +} diff --git a/lib/compiler.h b/lib/compiler.h index 21b1d08..d752e44 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -12,17 +12,22 @@ struct compiler struct status* status; }; -void compiler_init(struct compiler* self, +void compiler_init(struct compiler* self, struct status* status); void compiler_free(struct compiler* self); -void compiler_compile(struct compiler* self, +void compiler_compile(struct compiler* self, struct node* node, struct prog* prog, struct moka* moka); -void compiler_import(struct compiler* self, - struct moka* moka, +void compiler_import(struct compiler* self, + struct moka* moka, struct node* node); +void compiler_quote(struct compiler* self, + struct node* node, + struct prog* prog, + struct moka* moka); + #endif diff --git a/lib/exec.c b/lib/exec.c index cc42ae3..63d8553 100644 --- a/lib/exec.c +++ b/lib/exec.c @@ -37,6 +37,11 @@ void exec_instr(struct exec* self, switch (instr->opcode) { + case OP_MAKE_ARRAY: { + moka_make_array(moka, param); + self->pc++; + } break; + case OP_CALL: { moka_call(moka, param); self->pc++; diff --git a/lib/lexer.c b/lib/lexer.c index 198fe72..44b39af 100644 --- a/lib/lexer.c +++ b/lib/lexer.c @@ -19,6 +19,8 @@ void lexer_init(struct lexer* self, str_init(&self->separators); str_push(&self->separators, '('); str_push(&self->separators, ')'); + str_push(&self->separators, '['); + str_push(&self->separators, ']'); } void lexer_free(struct lexer* self) @@ -41,6 +43,16 @@ struct token* lexer_try_new_next(struct lexer* self) lexer_skip_spaces(self); lexer_skip_comments(self); + if ( (tok=lexer_try_new_text(self, TOKEN_OSQUARE, "[")) ) + { + return tok; + } + + if ( (tok=lexer_try_new_text(self, TOKEN_CSQUARE, "]")) ) + { + return tok; + } + if ( (tok=lexer_try_new_text(self, TOKEN_OPAR, "(")) ) { return tok; diff --git a/lib/moka.c b/lib/moka.c index d5de2fb..2a52623 100644 --- a/lib/moka.c +++ b/lib/moka.c @@ -70,7 +70,7 @@ void moka_import_module(struct moka* self, assert(self); assert(name); assert(module); - + struct symtable* mod_sym = &module->moka.symtable; struct env* env = mod_sym->root; @@ -98,41 +98,41 @@ void moka_import_module(struct moka* self, { case TY_NATIVE: { moka_decl_native( - self, - new_name, + self, + new_name, val->data.native->fun ); } break; case TY_SYMBOL: { moka_decl_var( - self, + self, new_name, - moka_push_symbol(self, + moka_push_symbol(self, val->data.sym, val->line)); } break; case TY_STRING: { moka_decl_var( - self, + self, new_name, - moka_push_string(self, + moka_push_string(self, val->data.str, val->line)); } break; case TY_INT: { moka_decl_var( - self, + self, new_name, - moka_push_int(self, + moka_push_int(self, val->data.integer, val->line)); } break; default: { - fprintf(stderr, + fprintf(stderr, "cannot import value of type <%s>\n", TypeKindStr[val->type]); abort(); @@ -246,7 +246,28 @@ void moka_dump(struct moka* self, MOKA value) { if (moka_is(self, value, TY_REF)) { - printf("&%zu", moka_get_ref(self, value)); + size_t ref = moka_get_ref(self, value); + struct value* val = self->global_values.data[ref]; + + if (val->type == TY_ARRAY) + { + printf("["); + for (size_t i=0; idata.array->values.size; i++) + { + if (i > 0) { printf(" "); } + MOKA v = (MOKA) val->data.array->values.data[i]; + moka_dump(self, v); + } + + printf("]"); + } + else + { + char buffer[MK_STRLEN]; + value_str(val, buffer, MK_STRLEN); + + printf("%s", buffer); + } return; } @@ -276,7 +297,7 @@ void moka_dump(struct moka* self, MOKA value) if (moka_is(self, value, TY_SYMBOL)) { - printf("'%s", moka_get_symbol(self, value)); + printf("%s", moka_get_symbol(self, value)); return; } @@ -287,6 +308,40 @@ void moka_dump(struct moka* self, MOKA value) abort(); } +MOKA moka_make_array(struct moka* self, int elements_count) +{ + struct value* value = malloc(sizeof(struct value)); + struct vec elements; + vec_init(&elements); + int line = 0; + struct frame* frame = moka_frame(self); + + for (int i=0; ilocal_values.data[val])->line; + vec_push(&elements, (void*) val); + } + + struct vec reverse; + vec_init(&reverse); + + for (size_t i=0; iglobal_values, value); + moka_push_ref(self, self->global_values.size - 1, line); + + vec_free(&elements); + vec_free(&reverse); + return 0; +} + MOKA moka_call(struct moka* self, int arg_count) { MOKA fun = moka_pop(self); diff --git a/lib/moka.h b/lib/moka.h index 0285d10..67891b5 100644 --- a/lib/moka.h +++ b/lib/moka.h @@ -63,6 +63,7 @@ TypeKind moka_type_of(struct moka* self, MOKA value); void moka_dump(struct moka* self, MOKA value); +MOKA moka_make_array(struct moka* self, int elements_count); MOKA moka_call(struct moka* self, int arg_count); MOKA moka_push(struct moka* self, MOKA value); diff --git a/lib/parser.c b/lib/parser.c index 48fbdef..f721da9 100644 --- a/lib/parser.c +++ b/lib/parser.c @@ -29,7 +29,7 @@ struct node* parser_try(struct parser* self, struct lex_context ctx = lexer_state(self->lexer); struct node* node = rule(self); - + if (node == NULL) { lexer_restore(self->lexer, ctx); @@ -50,7 +50,6 @@ struct node* parser_try_new_root(struct parser* self) if (!expr && !lexer_end(self->lexer)) { - printf("not at end: %c\n", self->lexer->source[self->lexer->context.cursor]); node_free(root); free(root); return NULL; @@ -71,9 +70,58 @@ struct node* parser_try_new_expr(struct parser* self) return MK_TRY(parser_try_new_call); } + if (lexer_next_is(self->lexer, TOKEN_OSQUARE, 0)) + { + return MK_TRY(parser_try_new_array); + } + return MK_TRY(parser_try_new_atom); } +struct node* parser_try_new_array(struct parser* self) +{ + if (!lexer_next_is(self->lexer, TOKEN_OSQUARE, 0)) + { + return NULL; + } + lexer_skip(self->lexer, TOKEN_OSQUARE); + + struct node* array = malloc(sizeof(struct node)); + node_init(array, NODE_CALL, NULL, self->lexer->context.line); + + while (!lexer_next_is(self->lexer, TOKEN_CSQUARE, 0)) + { + struct node* expr = MK_TRY(parser_try_new_expr); + + if (!expr) + { + node_free(array); free(array); + return NULL; + } + + node_add_new_child(array, expr); + } + + if (!lexer_next_is(self->lexer, TOKEN_CSQUARE, 0)) + { + return NULL; + } + lexer_skip(self->lexer, TOKEN_CSQUARE); + + struct node* node = malloc(sizeof(struct node)); + node_init(node, NODE_CALL, NULL, self->lexer->context.line); + + struct node* ident = malloc(sizeof(struct node)); + struct token* tok = malloc(sizeof(struct token)); + token_init(tok, TOKEN_IDENT, "quote"); + + node_init(ident, NODE_IDENT, tok, self->lexer->context.line); + node_add_new_child(node, ident); + node_add_new_child(node, array); + + return node; +} + struct node* parser_try_new_call(struct parser* self) { assert(self); @@ -86,7 +134,7 @@ struct node* parser_try_new_call(struct parser* self) lexer_skip(self->lexer, TOKEN_OPAR); struct node* target = MK_TRY(parser_try_new_expr); - + if (!target) { return NULL; @@ -185,7 +233,7 @@ struct node* parser_try_new_atom(struct parser* self) self->lexer->context.line); return node; } - + return NULL; } diff --git a/lib/parser.h b/lib/parser.h index 1246c6f..4617392 100644 --- a/lib/parser.h +++ b/lib/parser.h @@ -20,6 +20,7 @@ struct node* parser_try(struct parser* self, struct node* parser_try_new_root(struct parser* self); struct node* parser_try_new_expr(struct parser* self); +struct node* parser_try_new_array(struct parser* self); struct node* parser_try_new_call(struct parser* self); struct node* parser_try_new_atom(struct parser* self); diff --git a/lib/prog.c b/lib/prog.c index 880426c..5fc848b 100644 --- a/lib/prog.c +++ b/lib/prog.c @@ -40,6 +40,10 @@ size_t prog_add_instruction(struct prog* self, self->stack_addr++; } break; + case OP_MAKE_ARRAY: { + self->stack_addr += param; + } break; + default: { fprintf(stderr, "cannot find stack address of <%s>\n", OpcodeKindStr[opcode]); @@ -64,14 +68,14 @@ size_t prog_add_new_value(struct prog* self, void prog_dump(struct prog* self) { assert(self); - + printf("--- PROG ---\n"); for (size_t i=0; iinstructions.size; i++) { struct instruction const* instr = self->instructions.data[i]; - printf("%zu\t%s %zd\n", - i, - OpcodeKindStr[instr->opcode], + printf("%zu\t%s %zd\n", + i, + OpcodeKindStr[instr->opcode], instr->param); } } diff --git a/lib/prog.h b/lib/prog.h index d5f1a5e..eb392b7 100644 --- a/lib/prog.h +++ b/lib/prog.h @@ -9,7 +9,8 @@ G(OP_PUSH), \ G(OP_LOCAL_LOAD), \ G(OP_GLOBAL_LOAD), \ -G(OP_CALL) +G(OP_CALL), \ +G(OP_MAKE_ARRAY) MK_ENUM_H(OpcodeKind, OPCODE_KIND); diff --git a/lib/token.h b/lib/token.h index 453e3c6..d216fd1 100644 --- a/lib/token.h +++ b/lib/token.h @@ -8,7 +8,7 @@ G(TOKEN_INT), G(TOKEN_FLOAT), \ G(TOKEN_BOOL), G(TOKEN_STRING), \ G(TOKEN_SYMBOL), G(TOKEN_IDENT), \ G(TOKEN_OPAR), G(TOKEN_CPAR), \ -G(TOKEN_EOF) +G(TOKEN_OSQUARE), G(TOKEN_CSQUARE) MK_ENUM_H(TokenKind, TOKEN_KIND); diff --git a/lib/value.c b/lib/value.c index 77394e9..ba51d81 100644 --- a/lib/value.c +++ b/lib/value.c @@ -71,6 +71,89 @@ void value_init_lazy(struct value* self, struct node* value, int line) self->line = line; } +void value_init_array(struct value* self, struct vec* new_values, int line) +{ + assert(self); + assert(new_values); + + self->data.array = malloc(sizeof(struct array)); + array_init(self->data.array); + + for (size_t i=0; isize; i++) + { + array_push_value(self->data.array, (MOKA) new_values->data[i]); + } + + self->type = TY_ARRAY; + self->line = line; +} + +size_t value_str(struct value* self, char* buffer, size_t size) +{ + assert(self); + assert(buffer); + + switch (self->type) + { + case TY_INT: { + return snprintf(buffer, size, "%d", self->data.integer); + } break; + + case TY_FLOAT: { + return snprintf(buffer, size, "%f", self->data.real); + } break; + + case TY_BOOL: { + return snprintf(buffer, size, "%s", + self->data.boolean ? "true" : "false"); + } break; + + case TY_STRING: { + return snprintf(buffer, size, "%s", self->data.str); + } break; + + case TY_SYMBOL: { + return snprintf(buffer, size, "%s", self->data.sym); + } break; + + case TY_REF: { + return snprintf(buffer, size, "", self->data.ref); + } break; + + case TY_NATIVE: { + return snprintf(buffer, size, "", self->data.native); + } break; + + case TY_LAZY: { + return snprintf(buffer, size, "", + NodeKindStr[self->data.lazy->kind] + + strlen("NODE_")); + } break; + + case TY_ARRAY: { + size_t sz = 0; + sz += snprintf(buffer + sz, size - sz, "[array]"); + return sz; + } break; + + default: { + fprintf(stderr, "cannot stringify value <%s>\n", + TypeKindStr[self->type]); + abort(); + } break; + } + +// void value_init_int(struct value* self, int value, int line); +// void value_init_float(struct value* self, float value, int line); +// void value_init_bool(struct value* self, bool value, int line); +// void value_init_string(struct value* self, char const* value, int line); +// 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_init_array(struct value* self, struct vec* new_values, int line); +} + void value_free(struct value* self) { assert(self); @@ -90,4 +173,10 @@ void value_free(struct value* self) native_free(self->data.native); free(self->data.native); } + + if (self->type == TY_ARRAY) + { + array_free(self->data.array); + free(self->data.array); + } } diff --git a/lib/value.h b/lib/value.h index 8e7f832..0e39bd9 100644 --- a/lib/value.h +++ b/lib/value.h @@ -3,11 +3,12 @@ #include "commons.h" #include "native.h" +#include "array.h" #define TYPE_KIND(G) \ G(TY_INT), G(TY_FLOAT), G(TY_BOOL), \ G(TY_STRING), G(TY_SYMBOL), G(TY_REF), \ -G(TY_NATIVE), G(TY_LAZY) +G(TY_NATIVE), G(TY_LAZY), G(TY_ARRAY) MK_ENUM_H(TypeKind, TYPE_KIND); @@ -21,6 +22,7 @@ union value_data size_t ref; struct native* native; struct node* lazy; + struct array* array; }; struct value @@ -38,6 +40,9 @@ 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_init_array(struct value* self, struct vec* new_values, int line); + +size_t value_str(struct value* self, char* buffer, size_t size); void value_free(struct value* self); diff --git a/tests/lexer.h b/tests/lexer.h index bca2b59..b7c667a 100644 --- a/tests/lexer.h +++ b/tests/lexer.h @@ -84,11 +84,22 @@ START_TEST(lexer_funcall) ); } END_TEST + +START_TEST(lexer_array) +{ + test_lexer(" [] ", 2, + TOKEN_OSQUARE, "[", + TOKEN_CSQUARE, "]" + ); +} +END_TEST + void register_lexer(Suite* suite) { TCase* tcase = tcase_create("Lexer"); tcase_add_test(tcase, lexer_atom); tcase_add_test(tcase, lexer_funcall); + tcase_add_test(tcase, lexer_array); suite_add_tcase(suite, tcase); } diff --git a/tests/parser.h b/tests/parser.h index 90ad0bc..a372540 100644 --- a/tests/parser.h +++ b/tests/parser.h @@ -72,11 +72,22 @@ START_TEST(parser_call) } END_TEST +START_TEST(parser_array) +{ + test_parser("ROOT(CALL(IDENT[quote],CALL(INT[1],INT[2],INT[3])))", + " [1 2 3] "); + + test_parser("ROOT(CALL(IDENT[quote],CALL(INT[1])))", + " [1] "); +} +END_TEST + void register_parser(Suite* suite) { TCase* tcase = tcase_create("Parser"); tcase_add_test(tcase, parser_atom); tcase_add_test(tcase, parser_call); + tcase_add_test(tcase, parser_array); suite_add_tcase(suite, tcase); }