Mercurial > lbo > hg > ylisp
changeset 130:75ed83d94261
preprocess: Implement preprocessing of functions (defn)
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Sun, 01 Sep 2019 19:23:45 +0200 |
parents | e4c55f83eef0 |
children | 0af2b62f49d2 |
files | src/preprocess.c src/preprocess.h |
diffstat | 2 files changed, 117 insertions(+), 4 deletions(-) [+] |
line wrap: on
line diff
--- a/src/preprocess.c Sun Sep 01 19:22:31 2019 +0200 +++ b/src/preprocess.c Sun Sep 01 19:23:45 2019 +0200 @@ -1,10 +1,13 @@ #include "preprocess.h" #include "built-ins.h" +#include "func.h" #include "value.h" void ypreprocess(yexpr_t* expr) { - assert(false /* not yet implemented */); + ypreprocess_resolve_builtins(expr); + ypreprocess_defn(expr); + // ypreprocess_refs(expr); } void ypreprocess_resolve_builtins(yexpr_t* expr) { @@ -22,8 +25,7 @@ ybuiltin_translate(expr); break; case YEXPR_LIST: - if (expr->value.list.len == 0) - break; + if (expr->value.list.len == 0) break; yvec_t* list = &expr->value.list; for (size_t i = 0; i < list->len; i++) { ypreprocess_resolve_builtins(YVEC_AT(list, i, yexpr_t)); @@ -34,6 +36,108 @@ } } +/** + * @brief Resolve references to arguments in the function body. + */ +static void ypreprocess_resolve_inner_refs(yexpr_t* body, yvec_t* arg_descs) { + switch (body->typ) { + case YEXPR_REF: + if (yref_type(&body->value.ref) != YREF_SYM) break; + // Check if reference is to known argument. + for (size_t i = 0; i < arg_descs->len; i++) { + yarg_desc_t* arg = YVEC_AT(arg_descs, i, yarg_desc_t); + if (0 == ystr_cmp(&body->value.ref.ref.sym, &arg->argname)) { + yexpr_set_ref(body, yref_clone(&arg->argref)); + break; + } + } + break; + case YEXPR_LIST: + for (size_t i = 0; i < body->value.list.len; i++) { + yexpr_t* expr = YVEC_AT(&body->value.list, i, yexpr_t); + ypreprocess_resolve_inner_refs(expr, arg_descs); + } + break; + default: + break; + } +} + +/** + * @brief Store a `(defn ...)` as function and replace it with a YEXPR_UNDEF + * definition. + * + * `expr` must be a list starting with the `defn` built-in. + * + * Returns false if syntax is incorrect. + */ +static bool ypreprocess_compile_defn(yexpr_t* expr) { + yvec_t* list = &expr->value.list; + assert(4 <= list->len); + yexpr_t* defn = YVEC_AT(list, 0, yexpr_t); + yexpr_t* name = YVEC_AT(list, 1, yexpr_t); + yexpr_t* args = YVEC_AT(list, 2, yexpr_t); + if (defn->typ != YEXPR_BUILTIN || defn->value.builtin != YBUILTIN_DEFN) + return false; + if (name->typ != YEXPR_REF) return false; + assert(yref_type(&name->value.ref) == YREF_SYM); + if (args->typ != YEXPR_LIST) return false; + size_t n_args = args->value.list.len; + + yvec_t body_exprs = YVEC_NEW(yvec_at(list, 3), list->len - 3, yexpr_t); + yfunc_t func; + yvec_t arg_descs = YVEC_NEW(NULL, args->value.list.len, yarg_desc_t); + for (size_t i = 0; i < args->value.list.len; i++) { + yarg_desc_t desc; + yexpr_t* arg = YVEC_AT(&args->value.list, i, yexpr_t); + assert(arg->typ == YEXPR_REF && yref_type(&arg->value.ref) == YREF_SYM); + desc.argname = ystr_clone(&arg->value.ref.ref.sym); + desc.argref = yref_new_cix(n_args - 1 - i); + YVEC_PUSH(&arg_descs, &desc); + } + + func.name = name->value.ref.ref.sym; + func.args = arg_descs; + yexpr_t body = yexpr_new(); + yexpr_set_list(&body, body_exprs); // no destroy! + // Resolve references to arguments in body. + ypreprocess_resolve_inner_refs(&body, &arg_descs); + func.body = body_exprs; + + // Store function as value at the named reference. + yvalue_t func_value; + func_value.typ = YVALUE_FUNC; + func_value.value.func = func; + + yvalue_set(name->value.ref, &func_value); + + // Clean up and return. + yexpr_destroy(defn); + yexpr_destroy(args); + yvec_destroy(list); + *expr = yexpr_new(); // no destroy! + return true; +} + void ypreprocess_defn(yexpr_t* expr) { - // `defn`s are already translated to built-in exprs. + // `defn`s are already translated to built-in exprs by the resolve_builtins + // stage. Now we extract them, store them as func values into the value + // table, and replace them with `undef` expressions that will not be + // executed. + switch (expr->typ) { + case YEXPR_LIST: + if (expr->value.list.len >= 4) { + yexpr_t* first = YVEC_AT(&expr->value.list, 0, yexpr_t); + if (first->typ == YEXPR_BUILTIN && + first->value.builtin == YBUILTIN_DEFN) { + ypreprocess_compile_defn(expr); + return; + } + } + for (size_t i = 0; i < expr->value.list.len; i++) + ypreprocess_defn(YVEC_AT(&expr->value.list, i, yexpr_t)); + break; + default: + return; + } }
--- a/src/preprocess.h Sun Sep 01 19:22:31 2019 +0200 +++ b/src/preprocess.h Sun Sep 01 19:23:45 2019 +0200 @@ -30,6 +30,15 @@ void ypreprocess_defn(yexpr_t* expr); /** + * @brief Replace symbolic IDs with per-scope-unique numeric IDs. + * + * Run order: 3 + * + * NOTE: should not translate refs in e.g. defn (function name) + */ +void ypreprocess_refs(yexpr_t* expr); + +/** * @} */