diff options
-rw-r--r-- | compile.c | 43 | ||||
-rw-r--r-- | docs/content/3.manual/manual.yml | 5 | ||||
-rw-r--r-- | tests/all.test | 5 |
3 files changed, 51 insertions, 2 deletions
@@ -49,6 +49,9 @@ struct inst { struct inst* bound_by; char* symbol; + int nformals; + int nactuals; + block subfn; // used by CLOSURE_CREATE (body of function) block arglist; // used by CLOSURE_CREATE (formals) and CALL_JQ (arguments) @@ -66,6 +69,8 @@ static inst* inst_new(opcode op) { i->bytecode_pos = -1; i->bound_by = 0; i->symbol = 0; + i->nformals = -1; + i->nactuals = -1; i->subfn = gen_noop(); i->arglist = gen_noop(); i->source = UNKNOWN_LOCATION; @@ -210,6 +215,34 @@ int block_has_only_binders(block binders, int bindflags) { return 1; } +// Count a binder's (function) formal params +static int block_count_formals(block b) { + int args = 0; + if (b.first->op == CLOSURE_CREATE_C) + return b.first->imm.cfunc->nargs - 1; + for (inst* i = b.first->arglist.first; i; i = i->next) { + assert(i->op == CLOSURE_PARAM); + args++; + } + return args; +} + +// Count a call site's actual params +static int block_count_actuals(block b) { + int args = 0; + for (inst* i = b.first; i; i = i->next) { + switch (i->op) { + default: assert(0 && "Unknown function type"); break; + case CLOSURE_CREATE: + case CLOSURE_PARAM: + case CLOSURE_CREATE_C: + args++; + break; + } + } + return args; +} + static int block_bind_subblock(block binder, block body, int bindflags) { assert(block_is_single(binder)); assert((opcode_describe(binder.first->op)->flags & bindflags) == bindflags); @@ -217,6 +250,8 @@ static int block_bind_subblock(block binder, block body, int bindflags) { assert(binder.first->bound_by == 0 || binder.first->bound_by == binder.first); binder.first->bound_by = binder.first; + if (binder.first->nformals == -1) + binder.first->nformals = block_count_formals(binder); int nrefs = 0; for (inst* i = body.first; i; i = i->next) { int flags = opcode_describe(i->op)->flags; @@ -224,8 +259,12 @@ static int block_bind_subblock(block binder, block body, int bindflags) { i->bound_by == 0 && !strcmp(i->symbol, binder.first->symbol)) { // bind this instruction - i->bound_by = binder.first; - nrefs++; + if (i->op == CALL_JQ && i->nactuals == -1) + i->nactuals = block_count_actuals(i->arglist); + if (i->nactuals == -1 || i->nactuals == binder.first->nformals) { + i->bound_by = binder.first; + nrefs++; + } } // binding recurses into closures nrefs += block_bind_subblock(binder, i->subfn, bindflags); diff --git a/docs/content/3.manual/manual.yml b/docs/content/3.manual/manual.yml index 5bcce3ff..35396fb6 100644 --- a/docs/content/3.manual/manual.yml +++ b/docs/content/3.manual/manual.yml @@ -1549,6 +1549,11 @@ sections: With that definition, `addvalue(.foo)` will add the current input's `.foo` field to each element of the array. + Multiple definitions using the same function name are allowed. + Each re-definition replaces the previous one for the same + number of function arguments, but only for references from + functions (or main program) subsequent to the re-definition. + examples: - program: 'def addvalue(f): . + [f]; map(addvalue(.[0]))' input: '[[1,2],[10,20]]' diff --git a/tests/all.test b/tests/all.test index f70979cc..35293dc2 100644 --- a/tests/all.test +++ b/tests/all.test @@ -434,6 +434,11 @@ def f(x): x | x; f([.], . + [42]) [[1,2,3,42]] [1,2,3,42,42] +# test multiple function arities and redefinition +def f: .+1; def g: f; def f: .+100; def f(a):a+.+11; [(g|f(20)), f] +1 +[33,101] + # test closures and lexical scoping def id(x):x; 2000 as $x | def f(x):1 as $x | id([$x, x, x]); def g(x): 100 as $x | f($x,$x+x); g($x) "more testing" |