diff --git a/CHANGELOG.md b/CHANGELOG.md index 08dc6766..26646975 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ All notable changes to this project will be documented in this file. ## Unreleased - ??? +- Allow using `&named` in function prototypes for named arguments. This is a more ergonomic + variant of `&keys` that isn't as redundant, more self documenting, and allows extension to + things like default arguments. - Add `debugger` - an easy to use debugger function that just takes a fiber. - `dofile` will now start a debugger on errors if the environment it is passed has `:debug` set. - Add `debugger-on-status` function, which can be passed to `run-context` to start a debugger on diff --git a/src/core/asm.c b/src/core/asm.c index b82389fd..95785e48 100644 --- a/src/core/asm.c +++ b/src/core/asm.c @@ -553,6 +553,10 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int x = janet_get1(s, janet_ckeywordv("vararg")); if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_VARARG; + /* Check structarg */ + x = janet_get1(s, janet_ckeywordv("structarg")); + if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG; + /* Check source */ x = janet_get1(s, janet_ckeywordv("source")); if (janet_checktype(x, JANET_STRING)) def->source = janet_unwrap_string(x); @@ -884,6 +888,10 @@ static Janet janet_disasm_vararg(JanetFuncDef *def) { return janet_wrap_boolean(def->flags & JANET_FUNCDEF_FLAG_VARARG); } +static Janet janet_disasm_structarg(JanetFuncDef *def) { + return janet_wrap_boolean(def->flags & JANET_FUNCDEF_FLAG_STRUCTARG); +} + static Janet janet_disasm_constants(JanetFuncDef *def) { JanetArray *constants = janet_array(def->constants_length); for (int32_t i = 0; i < def->constants_length; i++) { @@ -933,6 +941,7 @@ Janet janet_disasm(JanetFuncDef *def) { janet_table_put(ret, janet_ckeywordv("bytecode"), janet_disasm_bytecode(def)); janet_table_put(ret, janet_ckeywordv("source"), janet_disasm_source(def)); janet_table_put(ret, janet_ckeywordv("vararg"), janet_disasm_vararg(def)); + janet_table_put(ret, janet_ckeywordv("structarg"), janet_disasm_structarg(def)); janet_table_put(ret, janet_ckeywordv("name"), janet_disasm_name(def)); janet_table_put(ret, janet_ckeywordv("slotcount"), janet_disasm_slotcount(def)); janet_table_put(ret, janet_ckeywordv("constants"), janet_disasm_constants(def)); @@ -986,6 +995,7 @@ JANET_CORE_FN(cfun_disasm, if (!janet_cstrcmp(kw, "source")) return janet_disasm_source(f->def); if (!janet_cstrcmp(kw, "name")) return janet_disasm_name(f->def); if (!janet_cstrcmp(kw, "vararg")) return janet_disasm_vararg(f->def); + if (!janet_cstrcmp(kw, "structarg")) return janet_disasm_structarg(f->def); if (!janet_cstrcmp(kw, "slotcount")) return janet_disasm_slotcount(f->def); if (!janet_cstrcmp(kw, "constants")) return janet_disasm_constants(f->def); if (!janet_cstrcmp(kw, "sourcemap")) return janet_disasm_sourcemap(f->def); diff --git a/src/core/specials.c b/src/core/specials.c index 6a56ff9d..1f91c3db 100644 --- a/src/core/specials.c +++ b/src/core/specials.c @@ -822,6 +822,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) { int selfref = 0; int seenamp = 0; int seenopt = 0; + int namedargs = 0; /* Begin function */ c->scope->flags |= JANET_SCOPE_CLOSURE; @@ -846,6 +847,9 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) { /* Keep track of destructured parameters */ JanetSlot *destructed_params = NULL; + JanetSlot *named_params = NULL; + JanetTable *named_table = NULL; + JanetSlot named_slot; /* Compile function parameters */ params = janet_unwrap_tuple(argv[parami]); @@ -853,49 +857,74 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) { arity = paramcount; for (i = 0; i < paramcount; i++) { Janet param = params[i]; - if (janet_checktype(param, JANET_SYMBOL)) { + if (namedargs) { + if (!janet_checktype(param, JANET_SYMBOL)) { + errmsg = "only named arguments can follow &named"; + goto error; + } + Janet key = janet_wrap_keyword(janet_unwrap_symbol(param)); + janet_table_put(named_table, key, param); + janet_v_push(named_params, janetc_farslot(c)); + } else if (janet_checktype(param, JANET_SYMBOL)) { /* Check for varargs and unfixed arity */ - if (!janet_cstrcmp(janet_unwrap_symbol(param), "&")) { - if (seenamp) { - errmsg = "& in unexpected location"; - goto error; - } else if (i == paramcount - 1) { - allow_extra = 1; + const uint8_t *sym = janet_unwrap_symbol(param); + if (sym[0] == '&') { + if (!janet_cstrcmp(sym, "&")) { + if (seenamp) { + errmsg = "& in unexpected location"; + goto error; + } else if (i == paramcount - 1) { + allow_extra = 1; + arity--; + } else if (i == paramcount - 2) { + vararg = 1; + arity -= 2; + } else { + errmsg = "& in unexpected location"; + goto error; + } + seenamp = 1; + } else if (!janet_cstrcmp(sym, "&opt")) { + if (seenopt) { + errmsg = "only one &opt allowed"; + goto error; + } else if (i == paramcount - 1) { + errmsg = "&opt cannot be last item in parameter list"; + goto error; + } + min_arity = i; arity--; - } else if (i == paramcount - 2) { - vararg = 1; - arity -= 2; - } else { - errmsg = "& in unexpected location"; - goto error; - } - seenamp = 1; - } else if (!janet_cstrcmp(janet_unwrap_symbol(param), "&opt")) { - if (seenopt) { - errmsg = "only one &opt allowed"; - goto error; - } else if (i == paramcount - 1) { - errmsg = "&opt cannot be last item in parameter list"; - goto error; - } - min_arity = i; - arity--; - seenopt = 1; - } else if (!janet_cstrcmp(janet_unwrap_symbol(param), "&keys")) { - if (seenamp) { - errmsg = "&keys in unexpected location"; - goto error; - } else if (i == paramcount - 2) { + seenopt = 1; + } else if (!janet_cstrcmp(sym, "&keys")) { + if (seenamp) { + errmsg = "&keys in unexpected location"; + goto error; + } else if (i == paramcount - 2) { + vararg = 1; + structarg = 1; + arity -= 2; + } else { + errmsg = "&keys in unexpected location"; + goto error; + } + seenamp = 1; + } else if (!janet_cstrcmp(sym, "&named")) { + if (seenamp) { + errmsg = "&named in unexpected location"; + goto error; + } vararg = 1; structarg = 1; - arity -= 2; + arity = i; + seenamp = 1; + namedargs = 1; + named_table = janet_table(10); + named_slot = janetc_farslot(c); } else { - errmsg = "&keys in unexpected location"; - goto error; + janetc_nameslot(c, sym, janetc_farslot(c)); } - seenamp = 1; } else { - janetc_nameslot(c, janet_unwrap_symbol(param), janetc_farslot(c)); + janetc_nameslot(c, sym, janetc_farslot(c)); } } else { janet_v_push(destructed_params, janetc_farslot(c)); @@ -914,6 +943,14 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) { } janet_v_free(destructed_params); + /* Compile named arguments */ + if (namedargs) { + Janet param = janet_wrap_table(named_table); + destructure(c, param, named_slot, defleaf, NULL); + janetc_freeslot(c, named_slot); + janet_v_free(named_params); + } + max_arity = (vararg || allow_extra) ? INT32_MAX : arity; if (!seenopt) min_arity = arity; diff --git a/test/suite0011.janet b/test/suite0011.janet index 34dd6c34..e2a96fbf 100644 --- a/test/suite0011.janet +++ b/test/suite0011.janet @@ -80,5 +80,12 @@ "table rawget regression" (table/new -1)) +# Named arguments +(defn named-arguments + [&named bob sally joe] + (+ bob sally joe)) + +(assert (= 15 (named-arguments :bob 3 :sally 5 :joe 7)) "named arguments 1") + (end-suite)