Mercurial > lbo > hg > ylisp
changeset 75:a7569b66b12a
built-ins: Implement first built-in function, let.
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Sun, 25 Aug 2019 18:46:25 +0200 |
parents | d5330e2b1488 |
children | 1ae2b63e9d65 |
files | src/CMakeLists.txt src/built-ins.c src/built-ins_test.c src/types.h |
diffstat | 4 files changed, 165 insertions(+), 23 deletions(-) [+] |
line wrap: on
line diff
--- a/src/CMakeLists.txt Sun Aug 25 18:46:06 2019 +0200 +++ b/src/CMakeLists.txt Sun Aug 25 18:46:25 2019 +0200 @@ -31,22 +31,28 @@ TARGET_LINK_LIBRARIES(atom_test core) YADD_TEST(atom_test) -# Value test. -ADD_EXECUTABLE(value_test value_test.c) -TARGET_LINK_LIBRARIES(value_test core) -YADD_TEST(value_test) +# Built-in test. +ADD_EXECUTABLE(builtin_test built-ins_test.c) +TARGET_LINK_LIBRARIES(builtin_test core) +YADD_TEST(builtin_test) # Expr test. ADD_EXECUTABLE(expr_test expr_test.c) TARGET_LINK_LIBRARIES(expr_test core) YADD_TEST(expr_test) +# Parse test +ADD_EXECUTABLE(parse_test parse_test.c) +TARGET_LINK_LIBRARIES(parse_test frontend) +YADD_TEST(parse_test) + # Print sizes. ADD_EXECUTABLE(sizes_test sizes_test.c) TARGET_LINK_LIBRARIES(sizes_test core) YADD_TEST(sizes_test) -# Parse test -ADD_EXECUTABLE(parse_test parse_test.c) -TARGET_LINK_LIBRARIES(parse_test frontend) -YADD_TEST(parse_test) +# Value test. +ADD_EXECUTABLE(value_test value_test.c) +TARGET_LINK_LIBRARIES(value_test core) +YADD_TEST(value_test) +
--- a/src/built-ins.c Sun Aug 25 18:46:06 2019 +0200 +++ b/src/built-ins.c Sun Aug 25 18:46:25 2019 +0200 @@ -1,25 +1,101 @@ #include "built-ins.h" -const char* YBUILTIN_ENUM_STR[] = { - "BUILTIN:UNDEF", - "BUILTIN:FOR", - "BUILTIN:LET", - "BUILTIN:DEFN", - "BUILTIN:+", - "BUILTIN:-", - "BUILTIN:*", - "BUILTIN:/", - "BUILTIN:IF", - "BUILTIN:SEQ", +#include <src/value.h> + +#include <src/base/str.h> + +// TODO: Write standard library! + +struct ybuiltin_id_mapping { + const char* id; + YBUILTIN_TYPE builtin; +}; + +static const struct ybuiltin_id_mapping YBUILTIN_ID_MAPPING[] = { + {"__invalid", YBUILTIN_UNDEF}, {"for", YBUILTIN_FOR}, {"let", YBUILTIN_LET}, + {"defn", YBUILTIN_DEFN}, {"+", YBUILTIN_PLUS}, {"-", YBUILTIN_MINUS}, + {"*", YBUILTIN_MULT}, {"/", YBUILTIN_DIV}, {"if", YBUILTIN_IF}, + {"seq", YBUILTIN_SEQ}, {"==", YBUILTIN_EQ}, {"<", YBUILTIN_LT}, +}; + +static const char* YBUILTIN_ENUM_STR[] = { + "BUILTIN:UNDEF", "BUILTIN:FOR", "BUILTIN:LET", "BUILTIN:DEFN", + + "BUILTIN:+", "BUILTIN:-", "BUILTIN:*", "BUILTIN:/", + + "BUILTIN:IF", "BUILTIN:SEQ", + + "BUILTIN:EQ", "BUILTIN:LT", }; +/// Ownership of msg is transferred to this function, ownership of offending is +/// not. +static yexpr_t ybuiltin_type_error(YBUILTIN_TYPE self, ystr_t msg, + yexpr_t* offending) { + ystr_t fullmsg = ystr_new(NULL); + ystr_t expr = yexpr_debug_str(offending); + ystr_build(&fullmsg, "%s: %s: %s", ybuiltin_name(self), ystr_str(&msg), + ystr_str(&expr)); + + yexpr_t exception = yexpr_new(); + yexpr_set_exception(&exception, fullmsg); + ystr_destroy(&expr); + ystr_destroy(&msg); + return exception; +} + +static YBUILTIN_TYPE ybuiltin_id(ystr_t* sym) { + const size_t builtins = + sizeof(YBUILTIN_ID_MAPPING) / sizeof(struct ybuiltin_id_mapping); + for (size_t i = 0; i < builtins; i++) { + if (0 == ystr_cmp_str(sym, YBUILTIN_ID_MAPPING[i].id)) { + return YBUILTIN_ID_MAPPING[i].builtin; + } + } + return YBUILTIN_UNDEF; +} + +static void nothing(void){}; + +/// Expects a reference and a value on the stack. They may not depend on other +/// references except for functions, i.e. should have been copied using +/// `yexpr_copy()` (which is the convention for calling functions anyway). +/// +/// Returns an UNDEF expression by default, or an EXCEPTION if something went +/// wrong. +yexpr_t ybuiltin_fn_let(yvec_t* call_stack) { + yexpr_t ref, val; + if (!yvec_pop(call_stack, &val)) goto notenoughfail; + if (!yvec_pop(call_stack, &ref)) goto notenoughfail; + + if (ref.typ != YEXPR_REF || val.typ == YEXPR_UNDEF) goto typefail; + yvalue_t newvalue = {.typ = YVALUE_EXPR, .value.expr = val}; + yvalue_set(ref.value.ref, &newvalue); + return yexpr_new(); +notenoughfail: + return ybuiltin_type_error( + YBUILTIN_LET, ystr_new("BUG: not enough arguments on call stack"), + NULL); +typefail: + nothing(); + yexpr_t args = yexpr_new(); + yexpr_init_or_extend(&args, ref); + yexpr_init_or_extend(&args, val); + yexpr_t exc = ybuiltin_type_error( + YBUILTIN_LET, + ystr_new( + "type mismatch: want (reference, value), received something else"), + &args); + yexpr_destroy(&args); + return exc; +} + /** * @brief Table of built-in functions, indexed by YBUILTIN_TYPE. * * TODO: write built-ins. */ -const ybuiltin_fn YBUILTIN_FNS[0]; - +static const ybuiltin_fn YBUILTIN_FNS[] = {NULL, NULL, ybuiltin_fn_let}; const char* ybuiltin_name(YBUILTIN_TYPE t) { assert(t * sizeof(const char*) < sizeof(YBUILTIN_ENUM_STR)); @@ -27,6 +103,7 @@ } yexpr_t ybuiltin_run(YBUILTIN_TYPE t, yvec_t* call_stack) { - assert(t * sizeof(ybuiltin_fn) < sizeof(YBUILTIN_FNS)); + assert((t * sizeof(ybuiltin_fn)) < sizeof(YBUILTIN_FNS)); return YBUILTIN_FNS[t](call_stack); } +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/built-ins_test.c Sun Aug 25 18:46:25 2019 +0200 @@ -0,0 +1,60 @@ +#include "built-ins.h" + +#include <src/atom.h> +#include <src/value.h> + +void test_builtin_let(void) { + yexpr_t ref_expr = yexpr_new(); + yref_t ref = yref_new(); + yexpr_set_ref(&ref_expr, ref); + yexpr_t val_expr = yexpr_new(); + yexpr_set_atom(&val_expr, yatom_get_or_add("my-atom")); + + // NOTE: The expressions should be copied, but for this test we can store + // them directly. + yvec_t stack; + YVEC_INIT(&stack, 4, yexpr_t); + YVEC_PUSH(&stack, &ref_expr); + YVEC_PUSH(&stack, &val_expr); + + yexpr_t result = ybuiltin_run(YBUILTIN_LET, &stack); + yexpr_debug(&result); + fputs("\n", stderr); + assert(result.typ == YEXPR_UNDEF); + assert(stack.len == 0); + + yvalue_t* stored = yvalue_get(ref); + assert(stored->typ == YVALUE_EXPR); + assert(stored->value.expr.typ == YEXPR_ATOM); + assert(stored->value.expr.value.atom == val_expr.value.atom); + + // Type mismatch exception. + yexpr_set_number(&ref_expr, 124); + YVEC_PUSH(&stack, &ref_expr); + YVEC_PUSH(&stack, &val_expr); + result = ybuiltin_run(YBUILTIN_LET, &stack); + yexpr_debug(&result); + fputs("\n", stderr); + assert(result.typ == YEXPR_EXCEPTION); + assert(stack.len == 0); + yexpr_destroy(&result); + + // Not enough arguments on stack. + result = ybuiltin_run(YBUILTIN_LET, &stack); + assert(result.typ == YEXPR_EXCEPTION); + fputs(ystr_str(&result.value.str), stderr); + assert(0 == + ystr_cmp_str( + &result.value.str, + "BUILTIN:LET: BUG: not enough arguments on call stack: NULL")); + + yexpr_destroy(&result); + yexpr_destroy(&ref_expr); + yexpr_destroy(&val_expr); + yvec_destroy(&stack); +}; + +int main(void) { + test_builtin_let(); + return 0; +}