diff options
-rw-r--r-- | ChangeLog | 11 | ||||
-rw-r--r-- | match.c | 1423 |
2 files changed, 755 insertions, 679 deletions
@@ -1,5 +1,16 @@ 2011-10-19 Kaz Kylheku <kaz@kylheku.com> + Task #11425 + + * match.c (v_accept_fail, v_next, v_parallel, v_collect, + v_flatten, v_forget_local, v_merge, v_bind, v_cat, v_output, + v_try, v_define, v_defex, v_throw, v_deffilter, v_eof): + New functions. + (match_files): Remaining directives moved to functions. + (dir_tables_init): New functions entered into hash table. + +2011-10-19 Kaz Kylheku <kaz@kylheku.com> + * hash.c (ll_hash): Hashing of pointers should take into account alignment, otherwise only values divisible by the alignment occur. This patch takes into considerations that @@ -1318,7 +1318,7 @@ typedef val (*v_match_func)(match_files_ctx c, match_files_ctx *cout); val s_p_ ## specline_var = first(spec); \ val specline_var = rest(s_p_ ## specline_var); \ val spec_linenum_var = first(s_p_ ## specline_var); \ - val first_spec = first(specline_var) + val first_spec_var = first(specline_var) static val v_skip(match_files_ctx c, match_files_ctx *cout) { @@ -1488,827 +1488,869 @@ static val v_block(match_files_ctx c, match_files_ctx *cout) return next_spec_k; } -static val match_files(match_files_ctx c) +static val v_accept_fail(match_files_ctx c, match_files_ctx *cout) { - gc_hint(c.data); + spec_bind (specline, spec_linenum, first_spec, c.spec); + val sym = first(first_spec); + val target = first(rest(first_spec)); - if (listp(c.data)) { /* recursive call with lazy list */ - ; /* no specia initialization */ - } else if (c.files) { /* c.data == t: toplevel call with file list */ - val source_spec = first(c.files); - val name = consp(source_spec) ? cdr(source_spec) : source_spec; - fpip_t fp = (errno = 0, complex_open(name, nil)); - val first_spec_item = second(first(c.spec)); + if (rest(specline)) + sem_error(spec_linenum, lit("unexpected material after ~a"), sym, nao); + + uw_block_return(target, + if2(sym == accept_s, + cons(c.bindings, + if3(c.data, cons(c.data, c.data_lineno), t)))); + /* TODO: uw_block_return could just throw this */ + if (target) + sem_error(spec_linenum, lit("~a: no block named ~a in scope"), + sym, target, nao); + else + sem_error(spec_linenum, lit("%~a: no anonymous block in scope"), + sym, nao); + return nil; +} - if (consp(first_spec_item) && eq(first(first_spec_item), next_s)) { - debugf(lit("not opening source ~a " - "since query starts with next directive"), name, nao); - } else { - debugf(lit("opening data source ~a"), name, nao); +static val v_next(match_files_ctx c, match_files_ctx *cout) +{ + spec_bind (specline, spec_linenum, first_spec, c.spec); + + if (rest(first_spec) && rest(specline)) + sem_error(spec_linenum, lit("invalid combination of old " + "and new next syntax"), nao); + if (rest(specline)) { + sem_error(spec_linenum, lit("obsolete next syntax: trailing material"), nao); + } - if (complex_open_failed(fp)) { - if (consp(source_spec) && car(source_spec) == nothrow_k) { - debugf(lit("could not open ~a: " - "treating as failed match due to nothrow"), name, nao); - return nil; - } else if (errno != 0) - file_err(nil, lit("could not open ~a (error ~a/~a)"), name, - num(errno), string_utf8(strerror(errno)), nao); - else - file_err(nil, lit("could not open ~a"), name, nao); + if ((c.spec = rest(c.spec)) == nil) + return cons(c.bindings, cons(c.data, c.data_lineno)); + + if (rest(first_spec)) { + val source = rest(first_spec); + val keyword = first(source); + val arg = keyword; + + if (keywordp(keyword)) { + if (eq(keyword, nothrow_k)) { + sem_error(spec_linenum, lit("misplaced :nothrow"), nao); + } else if (eq(keyword, args_k)) { + cons_bind (new_bindings, success, + match_files(mf_args(c))); + + if (success) + return cons(new_bindings, + if3(c.data, cons(c.data, c.data_lineno), t)); return nil; } + arg = second(source); + } - c.files = cons(name, cdr(c.files)); /* Get rid of cons and nothrow */ + { + val eval = eval_form(spec_linenum, arg, c.bindings); + val str = cdr(eval); - if ((c.data = complex_snarf(fp, name)) != nil) - c.data_lineno = num(1); + if (eq(second(source), nothrow_k)) { + if (str) { + c.files = cons(cons(nothrow_k, str), c.files); + } else { + c.files = rest(c.files); + if (!c.files) { + debuglf(spec_linenum, lit("next: out of arguments"), nao); + return nil; + } + c.files = cons(cons(nothrow_k, first(c.files)), rest(c.files)); + } + } else { + if (str) { + c.files = cons(str, c.files); + } else { + c.files = rest(c.files); + if (!c.files) + sem_error(spec_linenum, lit("next: out of arguments"), nao); + c.files = cons(cons(nothrow_k, first(c.files)), rest(c.files)); + } + } } - } else { /* toplevel call with no data or file list */ - c.data = nil; + } else { + c.files = rest(c.files); + if (!c.files) + sem_error(spec_linenum, lit("next: out of arguments"), nao); } - for (; c.spec; c.spec = rest(c.spec), - c.data = rest(c.data), - c.data_lineno = plus(c.data_lineno, num(1))) -repeat_spec_same_data: + /* We recursively process the file list, but the new + data position we return to the caller must be in the + original file we we were called with. Hence, we can't + make a straight tail call here. */ { - val specline = rest(first(c.spec)); - val spec_linenum = first(first(c.spec)); - val first_spec = first(specline); + cons_bind (new_bindings, success, match_files(mf_data(c, t, nil))); - if (consp(first_spec)) { - val sym = first(first_spec); - val entry = gethash(v_directive_table, sym); + if (success) + return cons(new_bindings, + if3(c.data, cons(c.data, c.data_lineno), t)); + return nil; + } +} - if (entry) { - v_match_func vmf = (v_match_func) cptr_get(entry); - match_files_ctx nc; - val result = vmf(c, &nc); +static val v_parallel(match_files_ctx c, match_files_ctx *cout) +{ + spec_bind (specline, spec_linenum, first_spec, c.spec); - if (result == next_spec_k) { - c = nc; - if ((c.spec = rest(c.spec)) == nil) - break; - goto repeat_spec_same_data; - } else if (result == decline_k) { - /* go on to other processing below */ - } else { - return result; - } - } else if (sym == fail_s || sym == accept_s) { - val target = first(rest(first_spec)); - - if (rest(specline)) - sem_error(spec_linenum, lit("unexpected material after ~a"), sym, nao); - - uw_block_return(target, - if2(sym == accept_s, - cons(c.bindings, - if3(c.data, cons(c.data, c.data_lineno), t)))); - /* TODO: uw_block_return could just throw this */ - if (target) - sem_error(spec_linenum, lit("~a: no block named ~a in scope"), - sym, target, nao); - else - sem_error(spec_linenum, lit("%~a: no anonymous block in scope"), - sym, nao); - return nil; - } else if (sym == next_s) { - if (rest(first_spec) && rest(specline)) - sem_error(spec_linenum, lit("invalid combination of old " - "and new next syntax"), nao); - if (rest(specline)) { - sem_error(spec_linenum, lit("obsolete next syntax: trailing material"), nao); - } + if (second(first_spec) == t) { + return decline_k; + } else { + val sym = first(first_spec); + val all_match = t; + val some_match = nil; + val max_line = zero; + val max_data = nil; + val specs = second(first_spec); + val plist = third(first_spec); + val choose_shortest = getplist(plist, shortest_k); + val choose_longest = getplist(plist, longest_k); + val choose_sym = or2(choose_longest, choose_shortest); + val choose_bindings = c.bindings, choose_line = zero, choose_data = nil; + val choose_minmax = choose_longest ? num(-1) : num(NUM_MAX); + val iter; - if ((c.spec = rest(c.spec)) == nil) - break; + if (choose_longest && choose_shortest) + sem_error(spec_linenum, lit("choose: both :shortest and :longest specified"), nao); - if (rest(first_spec)) { - val source = rest(first_spec); - val keyword = first(source); - val arg = keyword; - - if (keywordp(keyword)) { - if (eq(keyword, nothrow_k)) { - sem_error(spec_linenum, lit("misplaced :nothrow"), nao); - } else if (eq(keyword, args_k)) { - cons_bind (new_bindings, success, - match_files(mf_args(c))); - - if (success) - return cons(new_bindings, - if3(c.data, cons(c.data, c.data_lineno), t)); - return nil; - } - arg = second(source); - } + if (sym == choose_s && !choose_sym) + sem_error(spec_linenum, lit("choose: criterion not specified"), nao); - { - val eval = eval_form(spec_linenum, arg, c.bindings); - val str = cdr(eval); + for (iter = specs; iter != nil; iter = rest(iter)) + { + val nested_spec = first(iter); + cons_bind (new_bindings, success, + match_files(mf_spec(c, nested_spec))); - if (eq(second(source), nothrow_k)) { - if (str) { - c.files = cons(cons(nothrow_k, str), c.files); - } else { - c.files = rest(c.files); - if (!c.files) { - debuglf(spec_linenum, lit("next: out of arguments"), nao); - return nil; - } - c.files = cons(cons(nothrow_k, first(c.files)), rest(c.files)); - } - } else { - if (str) { - c.files = cons(str, c.files); + if (success) { + some_match = t; + + if (sym == choose_s) { + val binding = choose_sym ? assoc(new_bindings, choose_sym) : nil; + val value = cdr(binding); + + if (value) { + val len = length_str(value); + + if ((choose_longest && gt(len, choose_minmax)) || + (choose_shortest && lt(len, choose_minmax))) + { + choose_minmax = len; + choose_bindings = new_bindings; + + if (success == t) { + choose_data = t; } else { - c.files = rest(c.files); - if (!c.files) - sem_error(spec_linenum, lit("next: out of arguments"), nao); - c.files = cons(cons(nothrow_k, first(c.files)), rest(c.files)); + cons_bind (new_data, new_line, success); + choose_data = new_data; + choose_line = new_line; } } } } else { - c.files = rest(c.files); - if (!c.files) - sem_error(spec_linenum, lit("next: out of arguments"), nao); + /* choose does not propagate bindings between clauses! */ + c.bindings = new_bindings; } - /* We recursively process the file list, but the new - data position we return to the caller must be in the - original file we we were called with. Hence, we can't - make a straight tail call here. */ - { - cons_bind (new_bindings, success, match_files(mf_data(c, t, nil))); - if (success) - return cons(new_bindings, - if3(c.data, cons(c.data, c.data_lineno), t)); - return nil; + if (success == t) { + max_data = t; + } else if (consp(success) && max_data != t) { + cons_bind (new_data, new_line, success); + if (gt(new_line, max_line)) { + max_line = new_line; + max_data = new_data; + } } - } else if ((sym == some_s || sym == all_s || sym == none_s || - sym == maybe_s || sym == cases_s || sym == choose_s) && - second(first_spec) != t) - { - val all_match = t; - val some_match = nil; - val max_line = zero; - val max_data = nil; - val specs = second(first_spec); - val plist = third(first_spec); - val choose_shortest = getplist(plist, shortest_k); - val choose_longest = getplist(plist, longest_k); - val choose_sym = or2(choose_longest, choose_shortest); - val choose_bindings = c.bindings, choose_line = zero, choose_data = nil; - val choose_minmax = choose_longest ? num(-1) : num(NUM_MAX); - val iter; + if (sym == cases_s || sym == none_s) + break; + } else { + all_match = nil; + if (sym == all_s) + break; + } + } - if (choose_longest && choose_shortest) - sem_error(spec_linenum, lit("choose: both :shortest and :longest specified"), nao); + if (sym == all_s && !all_match) { + debuglf(spec_linenum, lit("all: some clauses didn't match"), nao); + return nil; + } - if (sym == choose_s && !choose_sym) - sem_error(spec_linenum, lit("choose: criterion not specified"), nao); + if ((sym == some_s || sym == cases_s) && !some_match) { + debuglf(spec_linenum, lit("some/cases: no clauses matched"), nao); + return nil; + } - for (iter = specs; iter != nil; iter = rest(iter)) - { - val nested_spec = first(iter); - cons_bind (new_bindings, success, - match_files(mf_spec(c, nested_spec))); + if (sym == none_s && some_match) { + debuglf(spec_linenum, lit("none: some clauses matched"), nao); + return nil; + } - if (success) { - some_match = t; + /* No check for maybe, since it always succeeds. */ - if (sym == choose_s) { - val binding = choose_sym ? assoc(new_bindings, choose_sym) : nil; - val value = cdr(binding); + if (choose_sym) { + if (consp(choose_data)) { + c.data_lineno = choose_line; + c.data = choose_data; + } else if (choose_data == t) { + c.data = nil; + } + c.bindings = choose_bindings; + } else if (consp(max_data)) { + c.data_lineno = max_line; + c.data = max_data; + } else if (max_data == t) { + c.data = nil; + } - if (value) { - val len = length_str(value); + *cout = c; + return next_spec_k; + } +} - if ((choose_longest && gt(len, choose_minmax)) || - (choose_shortest && lt(len, choose_minmax))) - { - choose_minmax = len; - choose_bindings = new_bindings; +static val v_collect(match_files_ctx c, match_files_ctx *cout) +{ + spec_bind (specline, spec_linenum, first_spec, c.spec); + val coll_spec = second(first_spec); + val until_last_spec = third(first_spec); + val args = fourth(first_spec); + val bindings_coll = nil; + val last_bindings = nil; + val max = getplist(args, maxgap_k); + val min = getplist(args, mingap_k); + val gap = getplist(args, gap_k); + val times = getplist(args, times_k); + val mintimes = getplist(args, mintimes_k); + val maxtimes = getplist(args, maxtimes_k); + val lines = getplist(args, lines_k); + val vars = getplist(args, vars_k); + cnum cmax = nump(gap) ? c_num(gap) : (nump(max) ? c_num(max) : 0); + cnum cmin = nump(gap) ? c_num(gap) : (nump(min) ? c_num(min) : 0); + cnum mincounter = cmin, maxcounter = 0; + cnum ctimax = nump(times) ? c_num(times) + : (nump(maxtimes) ? c_num(maxtimes) : 0); + cnum ctimin = nump(times) ? c_num(times) + : (nump(mintimes) ? c_num(mintimes) : 0); + cnum timescounter = 0, linescounter = 0; + cnum ctimes = nump(times) ? c_num(times) : 0; + cnum clines = nump(lines) ? c_num(lines) : 0; + val iter; - if (success == t) { - choose_data = t; - } else { - cons_bind (new_data, new_line, success); - choose_data = new_data; - choose_line = new_line; - } - } - } - } else { - /* choose does not propagate bindings between clauses! */ - c.bindings = new_bindings; - } + if (gap && (max || min)) + sem_error(spec_linenum, lit("collect: cannot mix :gap with :mingap or :maxgap"), nao); + + vars = vars_to_bindings(spec_linenum, vars, c.bindings); + + if ((times && ctimes == 0) || (lines && clines == 0)) { + *cout = c; + return next_spec_k; + } + + uw_block_begin(nil, result); + + result = t; + + while (c.data) { + val new_bindings = nil, success = nil; + + if ((gap || min) && mincounter < cmin) + goto next_collect; + + if (lines && linescounter++ >= clines) + break; + { + cons_set (new_bindings, success, + match_files(mf_spec(c, coll_spec))); + + /* Until/last clause sees un-collated bindings from collect. */ + if (until_last_spec) + { + cons_bind (sym, ul_spec, until_last_spec); + cons_bind (until_last_bindings, success, + match_files(mf_spec(c, ul_spec))); + if (success) { + debuglf(spec_linenum, lit("until/last matched ~a:~a"), + first(c.files), c.data_lineno, nao); + /* Until discards bindings and position, last keeps them. */ + if (sym == last_s) { + last_bindings = set_diff(until_last_bindings, + new_bindings, eq_f, nil); if (success == t) { - max_data = t; - } else if (consp(success) && max_data != t) { + c.data = t; + } else { cons_bind (new_data, new_line, success); - if (gt(new_line, max_line)) { - max_line = new_line; - max_data = new_data; - } + c.data = new_data; + c.data_lineno = new_line; } - if (sym == cases_s || sym == none_s) - break; - } else { - all_match = nil; - if (sym == all_s) - break; } + break; } + } - if (sym == all_s && !all_match) { - debuglf(spec_linenum, lit("all: some clauses didn't match"), nao); - return nil; - } + if (success) { + val strictly_new_bindings = set_diff(new_bindings, + c.bindings, eq_f, nil); - if ((sym == some_s || sym == cases_s) && !some_match) { - debuglf(spec_linenum, lit("some/cases: no clauses matched"), nao); - return nil; - } + debuglf(spec_linenum, lit("collect matched ~a:~a"), + first(c.files), c.data_lineno, nao); - if (sym == none_s && some_match) { - debuglf(spec_linenum, lit("none: some clauses matched"), nao); - return nil; - } + for (iter = vars; iter; iter = cdr(iter)) { + cons_bind (var, dfl, car(iter)); + val exists = assoc(new_bindings, var); - /* No check for maybe, since it always succeeds. */ - - if (choose_sym) { - if (consp(choose_data)) { - c.data_lineno = choose_line; - c.data = choose_data; - } else if (choose_data == t) { - c.data = nil; + if (!exists) { + if (!dfl) + sem_error(spec_linenum, lit("collect failed to bind ~a"), + var, nao); + else + strictly_new_bindings = acons(strictly_new_bindings, + var, dfl); } - c.bindings = choose_bindings; - } else if (consp(max_data)) { - c.data_lineno = max_line; - c.data = max_data; - } else if (max_data == t) { - c.data = nil; } - if ((c.spec = rest(c.spec)) == nil) - break; + for (iter = strictly_new_bindings; iter; iter = cdr(iter)) + { + val binding = car(iter); + val vars_binding = assoc(vars, car(binding)); - goto repeat_spec_same_data; - } else if (sym == collect_s) { - val coll_spec = second(first_spec); - val until_last_spec = third(first_spec); - val args = fourth(first_spec); - val bindings_coll = nil; - val last_bindings = nil; - val max = getplist(args, maxgap_k); - val min = getplist(args, mingap_k); - val gap = getplist(args, gap_k); - val times = getplist(args, times_k); - val mintimes = getplist(args, mintimes_k); - val maxtimes = getplist(args, maxtimes_k); - val lines = getplist(args, lines_k); - val vars = getplist(args, vars_k); - cnum cmax = nump(gap) ? c_num(gap) : (nump(max) ? c_num(max) : 0); - cnum cmin = nump(gap) ? c_num(gap) : (nump(min) ? c_num(min) : 0); - cnum mincounter = cmin, maxcounter = 0; - cnum ctimax = nump(times) ? c_num(times) - : (nump(maxtimes) ? c_num(maxtimes) : 0); - cnum ctimin = nump(times) ? c_num(times) - : (nump(mintimes) ? c_num(mintimes) : 0); - cnum timescounter = 0, linescounter = 0; - cnum ctimes = nump(times) ? c_num(times) : 0; - cnum clines = nump(lines) ? c_num(lines) : 0; - val iter; + if (!vars || vars_binding) { + val existing = assoc(bindings_coll, car(binding)); - if (gap && (max || min)) - sem_error(spec_linenum, lit("collect: cannot mix :gap with :mingap or :maxgap"), nao); + bindings_coll = acons_new(bindings_coll, car(binding), + cons(cdr(binding), cdr(existing))); + } + } + } - vars = vars_to_bindings(spec_linenum, vars, c.bindings); + if (success) { + if (consp(success)) { + cons_bind (new_data, new_line, success); - if ((times && ctimes == 0) || (lines && clines == 0)) { - if ((c.spec = rest(c.spec)) == nil) - break; + bug_unless (ge(new_line, c.data_lineno)); - goto repeat_spec_same_data; - } + if (new_line == c.data_lineno) { + new_data = cdr(new_data); + new_line = plus(new_line, num(1)); + } - uw_block_begin(nil, result); + debuglf(spec_linenum, lit("collect advancing from line ~a to ~a"), + c.data_lineno, new_line, nao); - result = t; + c.data = new_data; + c.data_lineno = new_line; + *car_l(success) = nil; + } else { + debuglf(spec_linenum, lit("collect consumed entire file"), nao); + c.data = nil; + } + mincounter = 0; + maxcounter = 0; - while (c.data) { - val new_bindings = nil, success = nil; + timescounter++; - if ((gap || min) && mincounter < cmin) - goto next_collect; + if ((times || maxtimes) && timescounter >= ctimax) + break; + } else { +next_collect: + mincounter++; + if ((gap || max) && ++maxcounter > cmax) + break; + c.data_lineno = plus(c.data_lineno, num(1)); + c.data = rest(c.data); + } + } + } - if (lines && linescounter++ >= clines) - break; + uw_block_end; - { - cons_set (new_bindings, success, - match_files(mf_spec(c, coll_spec))); + if (!result) { + debuglf(spec_linenum, lit("collect explicitly failed"), nao); + return nil; + } - /* Until/last clause sees un-collated bindings from collect. */ - if (until_last_spec) - { - cons_bind (sym, ul_spec, until_last_spec); - cons_bind (until_last_bindings, success, - match_files(mf_spec(c, ul_spec))); - - if (success) { - debuglf(spec_linenum, lit("until/last matched ~a:~a"), - first(c.files), c.data_lineno, nao); - /* Until discards bindings and position, last keeps them. */ - if (sym == last_s) { - last_bindings = set_diff(until_last_bindings, - new_bindings, eq_f, nil); - if (success == t) { - c.data = t; - } else { - cons_bind (new_data, new_line, success); - c.data = new_data; - c.data_lineno = new_line; - } - } - break; - } - } + if ((times || mintimes) && timescounter < ctimin) { + debuglf(spec_linenum, lit("fewer than ~a iterations collected"), + num(ctimin), nao); + return nil; + } - if (success) { - val strictly_new_bindings = set_diff(new_bindings, - c.bindings, eq_f, nil); + if (!bindings_coll) + debuglf(spec_linenum, lit("nothing was collected"), nao); - debuglf(spec_linenum, lit("collect matched ~a:~a"), - first(c.files), c.data_lineno, nao); + c.bindings = set_diff(c.bindings, bindings_coll, eq_f, car_f); - for (iter = vars; iter; iter = cdr(iter)) { - cons_bind (var, dfl, car(iter)); - val exists = assoc(new_bindings, var); + for (iter = bindings_coll; iter; iter = cdr(iter)) { + val pair = car(iter); + val rev = cons(car(pair), nreverse(cdr(pair))); + c.bindings = cons(rev, c.bindings); + } - if (!exists) { - if (!dfl) - sem_error(spec_linenum, lit("collect failed to bind ~a"), - var, nao); - else - strictly_new_bindings = acons(strictly_new_bindings, - var, dfl); - } - } + if (last_bindings) { + c.bindings = set_diff(c.bindings, last_bindings, eq_f, car_f); + c.bindings = nappend2(last_bindings, c.bindings); + } - for (iter = strictly_new_bindings; iter; iter = cdr(iter)) - { - val binding = car(iter); - val vars_binding = assoc(vars, car(binding)); + *cout = c; + return next_spec_k; +} - if (!vars || vars_binding) { - val existing = assoc(bindings_coll, car(binding)); +static val v_flatten(match_files_ctx c, match_files_ctx *cout) +{ + spec_bind (specline, spec_linenum, first_spec, c.spec); + val iter; - bindings_coll = acons_new(bindings_coll, car(binding), - cons(cdr(binding), cdr(existing))); - } - } - } + for (iter = rest(first_spec); iter; iter = rest(iter)) { + val sym = first(iter); - if (success) { - if (consp(success)) { - cons_bind (new_data, new_line, success); + if (!bindable(sym)) { + sem_error(spec_linenum, + lit("flatten: ~s is not a bindable symbol"), sym, nao); + } else { + val existing = assoc(c.bindings, sym); - bug_unless (ge(new_line, c.data_lineno)); + if (existing) + *cdr_l(existing) = flatten(cdr(existing)); + } + } - if (new_line == c.data_lineno) { - new_data = cdr(new_data); - new_line = plus(new_line, num(1)); - } + *cout = c; + return next_spec_k; +} - debuglf(spec_linenum, lit("collect advancing from line ~a to ~a"), - c.data_lineno, new_line, nao); +static val v_forget_local(match_files_ctx c, match_files_ctx *cout) +{ + val specline = rest(first(c.spec)); + val first_spec = first(specline); + c.bindings = alist_remove(c.bindings, rest(first_spec)); + *cout = c; + return next_spec_k; +} - c.data = new_data; - c.data_lineno = new_line; - *car_l(success) = nil; - } else { - debuglf(spec_linenum, lit("collect consumed entire file"), nao); - c.data = nil; - } - mincounter = 0; - maxcounter = 0; +static val v_merge(match_files_ctx c, match_files_ctx *cout) +{ + spec_bind (specline, spec_linenum, first_spec, c.spec); + val target = first(rest(first_spec)); + val args = rest(rest(first_spec)); + val merged = nil; - timescounter++; + if (!bindable(target)) + sem_error(spec_linenum, lit("merge: ~s is not a bindable symbol"), + target, nao); - if ((times || maxtimes) && timescounter >= ctimax) - break; - } else { - next_collect: - mincounter++; - if ((gap || max) && ++maxcounter > cmax) - break; - c.data_lineno = plus(c.data_lineno, num(1)); - c.data = rest(c.data); - } - } - } + for (; args; args = rest(args)) { + val arg = first(args); - uw_block_end; + if (arg) { + val arg_eval = eval_form(spec_linenum, arg, c.bindings); - if (!result) { - debuglf(spec_linenum, lit("collect explicitly failed"), nao); - return nil; - } + if (merged) + merged = weird_merge(merged, cdr(arg_eval)); + else + merged = cdr(arg_eval); + } + } - if ((times || mintimes) && timescounter < ctimin) { - debuglf(spec_linenum, lit("fewer than ~a iterations collected"), - num(ctimin), nao); - return nil; - } + c.bindings = acons_new(c.bindings, target, merged); - if (!bindings_coll) - debuglf(spec_linenum, lit("nothing was collected"), nao); + *cout = c; + return next_spec_k; +} - c.bindings = set_diff(c.bindings, bindings_coll, eq_f, car_f); +static val v_bind(match_files_ctx c, match_files_ctx *cout) +{ + spec_bind (specline, spec_linenum, first_spec, c.spec); + val args = rest(first_spec); + val pattern = first(args); + val form = second(args); + val val = eval_form(spec_linenum, form, c.bindings); - for (iter = bindings_coll; iter; iter = cdr(iter)) { - val pair = car(iter); - val rev = cons(car(pair), nreverse(cdr(pair))); - c.bindings = cons(rev, c.bindings); - } + c.bindings = dest_bind(spec_linenum, c.bindings, pattern, cdr(val)); - if (last_bindings) { - c.bindings = set_diff(c.bindings, last_bindings, eq_f, car_f); - c.bindings = nappend2(last_bindings, c.bindings); - } + if (c.bindings == t) + return nil; - if ((c.spec = rest(c.spec)) == nil) - break; + *cout = c; + return next_spec_k; +} - goto repeat_spec_same_data; - } else if (sym == flatten_s) { - val iter; +static val v_cat(match_files_ctx c, match_files_ctx *cout) +{ + spec_bind (specline, spec_linenum, first_spec, c.spec); + val iter; - for (iter = rest(first_spec); iter; iter = rest(iter)) { - val sym = first(iter); + for (iter = rest(first_spec); iter; iter = rest(iter)) { + val sym = first(iter); - if (!bindable(sym)) { - sem_error(spec_linenum, - lit("flatten: ~s is not a bindable symbol"), sym, nao); - } else { - val existing = assoc(c.bindings, sym); + if (!bindable(sym)) { + sem_error(spec_linenum, + lit("cat: ~s is not a bindable symbol"), sym, nao); + } else { + val existing = assoc(c.bindings, sym); + val sep = nil; - if (existing) - *cdr_l(existing) = flatten(cdr(existing)); - } - } + if (rest(specline)) { + val sub = subst_vars(rest(specline), c.bindings, nil); + sep = cat_str(sub, nil); + } - if ((c.spec = rest(c.spec)) == nil) - break; + if (existing) + *cdr_l(existing) = cat_str(flatten(cdr(existing)), sep); + } + } - goto repeat_spec_same_data; - } else if (sym == forget_s || sym == local_s) { - c.bindings = alist_remove(c.bindings, rest(first_spec)); + *cout = c; + return next_spec_k; +} - if ((c.spec = rest(c.spec)) == nil) - break; +static val v_output(match_files_ctx c, match_files_ctx *cout) +{ + spec_bind (specline, spec_linenum, first_spec, c.spec); + val specs = second(first_spec); + val dest_spec = third(first_spec); + val nothrow = nil; + val dest = lit("-"); + val filter = nil; + fpip_t fp; + + if (eq(first(dest_spec), nothrow_k)) { + if (rest(dest_spec)) + sem_error(spec_linenum, lit("material after :nothrow in output"), nao); + } else if (!keywordp(first(dest_spec))) { + val form = first(dest_spec); + val val = eval_form(spec_linenum, form, c.bindings); + dest = or2(cdr(val), dest); + pop(&dest_spec); + } - goto repeat_spec_same_data; - } else if (sym == merge_s) { - val target = first(rest(first_spec)); - val args = rest(rest(first_spec)); - val merged = nil; + if (eq(first(dest_spec), nothrow_k)) { + nothrow = t; + pop(&dest_spec); + } - if (!bindable(target)) - sem_error(spec_linenum, lit("~a: ~s is not a bindable symbol"), - sym, target, nao); + if (keywordp(first(dest_spec))) { + val filter_sym = getplist(dest_spec, filter_k); - for (; args; args = rest(args)) { - val arg = first(args); + if (filter_sym) { + filter = get_filter_trie(filter_sym); - if (arg) { - val arg_eval = eval_form(spec_linenum, arg, c.bindings); + if (!filter) + sem_error(spec_linenum, lit("unknown filter ~s"), filter_sym, nao); + } + } - if (merged) - merged = weird_merge(merged, cdr(arg_eval)); - else - merged = cdr(arg_eval); - } - } + fp = (errno = 0, complex_open(dest, t)); - c.bindings = acons_new(c.bindings, target, merged); + debugf(lit("opening data sink ~a"), dest, nao); - if ((c.spec = rest(c.spec)) == nil) - break; + if (complex_open_failed(fp)) { + if (nothrow) { + debugf(lit("could not open ~a: " + "treating as failed match due to nothrow"), dest, nao); + return nil; + } else if (errno != 0) { + file_err(nil, lit("could not open ~a (error ~a/~a)"), dest, + num(errno), string_utf8(strerror(errno)), nao); + } else { + file_err(nil, lit("could not open ~a"), dest, nao); + } + } else { + val stream = complex_stream(fp, dest); + do_output(c.bindings, specs, filter, stream); + close_stream(stream, t); + } - goto repeat_spec_same_data; - } else if (sym == bind_s) { - val args = rest(first_spec); - val pattern = first(args); - val form = second(args); - val val = eval_form(spec_linenum, form, c.bindings); + *cout = c; + return next_spec_k; +} - c.bindings = dest_bind(spec_linenum, c.bindings, pattern, cdr(val)); +static val v_try(match_files_ctx c, match_files_ctx *cout) +{ + spec_bind (specline, spec_linenum, first_spec, c.spec); + val catch_syms = second(first_spec); + val try_clause = third(first_spec); + val catch_fin = fourth(first_spec); + val finally_clause = nil; - if (c.bindings == t) - return nil; + { + uw_block_begin(nil, result); + uw_catch_begin(catch_syms, exsym, exvals); - if ((c.spec = rest(c.spec)) == nil) - break; + result = match_files(mf_spec(c, try_clause)); + uw_do_unwind; - goto repeat_spec_same_data; - } else if (sym == cat_s) { + uw_catch(exsym, exvals) { + { val iter; - for (iter = rest(first_spec); iter; iter = rest(iter)) { - val sym = first(iter); + for (iter = catch_fin; iter; iter = cdr(iter)) { + val clause = car(iter); + val type = first(second(clause)); + val params = second(second(clause)); + val body = third(clause); + val vals = if3(listp(exvals), + exvals, + cons(cons(t, exvals), nil)); + + if (first(clause) == catch_s) { + if (uw_exception_subtype_p(exsym, type)) { + val all_bind = t; + val piter, viter; + + for (piter = params, viter = vals; + piter && viter; + piter = cdr(piter), viter = cdr(viter)) + { + val param = car(piter); + val value = car(viter); - if (!bindable(sym)) { - sem_error(spec_linenum, - lit("cat: ~s is not a bindable symbol"), sym, nao); - } else { - val existing = assoc(c.bindings, sym); - val sep = nil; + if (value) { + c.bindings = dest_bind(spec_linenum, c.bindings, + param, cdr(value)); - if (rest(specline)) { - val sub = subst_vars(rest(specline), c.bindings, nil); - sep = cat_str(sub, nil); - } + if (c.bindings == t) { + all_bind = nil; + break; + } + } + } - if (existing) - *cdr_l(existing) = cat_str(flatten(cdr(existing)), sep); + if (all_bind) { + cons_bind (new_bindings, success, + match_files(mf_spec(c, body))); + if (success) { + c.bindings = new_bindings; + result = t; /* catch succeeded, so try succeeds */ + if (consp(success)) { + c.data = car(success); + c.data_lineno = cdr(success); + } else { + c.data = nil; + } + } + } + break; + } + } else if (car(clause) == finally_s) { + finally_clause = body; } } + } + uw_do_unwind; + } - if ((c.spec = rest(c.spec)) == nil) - break; - - goto repeat_spec_same_data; - } else if (sym == output_s) { - val specs = second(first_spec); - val dest_spec = third(first_spec); - val nothrow = nil; - val dest = lit("-"); - val filter = nil; - fpip_t fp; - - if (eq(first(dest_spec), nothrow_k)) { - if (rest(dest_spec)) - sem_error(spec_linenum, lit("material after :nothrow in output"), nao); - } else if (!keywordp(first(dest_spec))) { - val form = first(dest_spec); - val val = eval_form(spec_linenum, form, c.bindings); - dest = or2(cdr(val), dest); - pop(&dest_spec); + uw_unwind { + val iter; + + /* result may be t, from catch above. */ + if (consp(result)) { + /* We process it before finally, as part of the unwinding, so + finally can accumulate more bindings over top of any bindings + produced by the main clause. */ + cons_bind (new_bindings, success, result); + if (consp(success)) { + c.data = car(success); + c.data_lineno = cdr(success); + } else { + c.data = nil; } + c.bindings = new_bindings; + } - if (eq(first(dest_spec), nothrow_k)) { - nothrow = t; - pop(&dest_spec); + if (!finally_clause) { + for (iter = catch_fin; iter; iter = cdr(iter)) { + val clause = car(iter); + if (first(clause) == finally_s) { + finally_clause = third(clause); + break; + } } + } - if (keywordp(first(dest_spec))) { - val filter_sym = getplist(dest_spec, filter_k); - - if (filter_sym) { - filter = get_filter_trie(filter_sym); + if (finally_clause) { + cons_bind (new_bindings, success, + match_files(mf_spec(c, finally_clause))); - if (!filter) - sem_error(spec_linenum, lit("unknown filter ~s"), filter_sym, nao); + if (success) { + c.bindings = new_bindings; + result = t; /* finally succeeds, so try block succeeds */ + if (consp(success)) { + c.data = car(success); + c.data_lineno = cdr(success); + } else { + c.data = nil; } } + } + } - fp = (errno = 0, complex_open(dest, t)); + uw_catch_end; + uw_block_end; - debugf(lit("opening data sink ~a"), dest, nao); + if (!result) + return nil; - if (complex_open_failed(fp)) { - if (nothrow) { - debugf(lit("could not open ~a: " - "treating as failed match due to nothrow"), dest, nao); - return nil; - } else if (errno != 0) { - file_err(nil, lit("could not open ~a (error ~a/~a)"), dest, - num(errno), string_utf8(strerror(errno)), nao); - } else { - file_err(nil, lit("could not open ~a"), dest, nao); - } - } else { - val stream = complex_stream(fp, dest); - do_output(c.bindings, specs, filter, stream); - close_stream(stream, t); - } + *cout = c; + return next_spec_k; + } +} - if ((c.spec = rest(c.spec)) == nil) - break; +static val v_define(match_files_ctx c, match_files_ctx *cout) +{ + spec_bind (specline, spec_linenum, first_spec, c.spec); + val args = second(first_spec); + val body = third(first_spec); + val name = first(args); + val params = second(args); - goto repeat_spec_same_data; - } else if (sym == define_s) { - val args = second(first_spec); - val body = third(first_spec); - val name = first(args); - val params = second(args); + if (rest(specline)) + sem_error(spec_linenum, + lit("unexpected material after define"), nao); - if (rest(specline)) - sem_error(spec_linenum, - lit("unexpected material after define"), nao); + uw_set_func(name, cons(params, body)); - uw_set_func(name, cons(params, body)); + *cout = c; + return next_spec_k; +} - if ((c.spec = rest(c.spec)) == nil) - break; +static val v_defex(match_files_ctx c, match_files_ctx *cout) +{ + spec_bind (specline, spec_linenum, first_spec, c.spec); + val types = rest(first_spec); + if (!all_satisfy(types, func_n1(symbolp), nil)) + sem_error(spec_linenum, lit("defex arguments must all be symbols"), + nao); + (void) reduce_left(func_n2(uw_register_subtype), types, nil, nil); - goto repeat_spec_same_data; - } else if (sym == try_s) { - val catch_syms = second(first_spec); - val try_clause = third(first_spec); - val catch_fin = fourth(first_spec); - val finally_clause = nil; + *cout = c; + return next_spec_k; +} - { - uw_block_begin(nil, result); - uw_catch_begin(catch_syms, exsym, exvals); +static val v_throw(match_files_ctx c, match_files_ctx *cout) +{ + spec_bind (specline, spec_linenum, first_spec, c.spec); + val type = second(first_spec); + val args = rest(rest(first_spec)); + if (!symbolp(type)) + sem_error(spec_linenum, lit("throw: ~a is not a type symbol"), + type, nao); + { + val values = mapcar(curry_123_2(func_n3(eval_form), + spec_linenum, c.bindings), args); + uw_throw(type, values); + } +} - result = match_files(mf_spec(c, try_clause)); - uw_do_unwind; +static val v_deffilter(match_files_ctx c, match_files_ctx *cout) +{ + spec_bind (specline, spec_linenum, first_spec, c.spec); + val sym = second(first_spec); + val table = rest(rest(first_spec)); - uw_catch(exsym, exvals) { - { - val iter; - - for (iter = catch_fin; iter; iter = cdr(iter)) { - val clause = car(iter); - val type = first(second(clause)); - val params = second(second(clause)); - val body = third(clause); - val vals = if3(listp(exvals), - exvals, - cons(cons(t, exvals), nil)); - - if (first(clause) == catch_s) { - if (uw_exception_subtype_p(exsym, type)) { - val all_bind = t; - val piter, viter; - - for (piter = params, viter = vals; - piter && viter; - piter = cdr(piter), viter = cdr(viter)) - { - val param = car(piter); - val value = car(viter); - - if (value) { - c.bindings = dest_bind(spec_linenum, c.bindings, - param, cdr(value)); - - if (c.bindings == t) { - all_bind = nil; - break; - } - } - } + if (!symbolp(sym)) + sem_error(spec_linenum, lit("deffilter: ~a is not a symbol"), + first(first_spec), nao); - if (all_bind) { - cons_bind (new_bindings, success, - match_files(mf_spec(c, body))); - if (success) { - c.bindings = new_bindings; - result = t; /* catch succeeded, so try succeeds */ - if (consp(success)) { - c.data = car(success); - c.data_lineno = cdr(success); - } else { - c.data = nil; - } - } - } - break; - } - } else if (car(clause) == finally_s) { - finally_clause = body; - } - } - } - uw_do_unwind; - } + if (!all_satisfy(table, andf(func_n1(listp), + chain(func_n1(length), + curry_12_2(func_n2(eq), two), + nao), + chain(func_n1(first), + func_n1(stringp), + nao), + chain(func_n1(second), + func_n1(stringp), + nao), + nao), + nil)) + sem_error(spec_linenum, + lit("deffilter arguments must be string pairs"), + nao); + register_filter(sym, table); + /* TODO: warn about replaced filter. */ + *cout = c; + return next_spec_k; +} - uw_unwind { - val iter; +static val v_eof(match_files_ctx c, match_files_ctx *cout) +{ + if (c.data) { + debugf(lit("eof failed to match at ~a"), c.data_lineno, nao); + return nil; + } + *cout = c; + return next_spec_k; +} - /* result may be t, from catch above. */ - if (consp(result)) { - /* We process it before finally, as part of the unwinding, so - finally can accumulate more bindings over top of any bindings - produced by the main clause. */ - cons_bind (new_bindings, success, result); - if (consp(success)) { - c.data = car(success); - c.data_lineno = cdr(success); - } else { - c.data = nil; - } - c.bindings = new_bindings; - } +static val match_files(match_files_ctx c) +{ + gc_hint(c.data); - if (!finally_clause) { - for (iter = catch_fin; iter; iter = cdr(iter)) { - val clause = car(iter); - if (first(clause) == finally_s) { - finally_clause = third(clause); - break; - } - } - } + if (listp(c.data)) { /* recursive call with lazy list */ + ; /* no specia initialization */ + } else if (c.files) { /* c.data == t: toplevel call with file list */ + val source_spec = first(c.files); + val name = consp(source_spec) ? cdr(source_spec) : source_spec; + fpip_t fp = (errno = 0, complex_open(name, nil)); + val first_spec_item = second(first(c.spec)); - if (finally_clause) { - cons_bind (new_bindings, success, - match_files(mf_spec(c, finally_clause))); + if (consp(first_spec_item) && eq(first(first_spec_item), next_s)) { + debugf(lit("not opening source ~a " + "since query starts with next directive"), name, nao); + } else { + debugf(lit("opening data source ~a"), name, nao); - if (success) { - c.bindings = new_bindings; - result = t; /* finally succeeds, so try block succeeds */ - if (consp(success)) { - c.data = car(success); - c.data_lineno = cdr(success); - } else { - c.data = nil; - } - } - } - } + if (complex_open_failed(fp)) { + if (consp(source_spec) && car(source_spec) == nothrow_k) { + debugf(lit("could not open ~a: " + "treating as failed match due to nothrow"), name, nao); + return nil; + } else if (errno != 0) + file_err(nil, lit("could not open ~a (error ~a/~a)"), name, + num(errno), string_utf8(strerror(errno)), nao); + else + file_err(nil, lit("could not open ~a"), name, nao); + return nil; + } - uw_catch_end; - uw_block_end; + c.files = cons(name, cdr(c.files)); /* Get rid of cons and nothrow */ - if (!result) - return nil; + if ((c.data = complex_snarf(fp, name)) != nil) + c.data_lineno = num(1); + } + } else { /* toplevel call with no data or file list */ + c.data = nil; + } + for (; c.spec; c.spec = rest(c.spec), + c.data = rest(c.data), + c.data_lineno = plus(c.data_lineno, num(1))) +repeat_spec_same_data: + { + spec_bind (specline, spec_linenum, first_spec, c.spec); + + if (consp(first_spec)) { + val sym = first(first_spec); + val entry = gethash(v_directive_table, sym); + + if (entry) { + v_match_func vmf = (v_match_func) cptr_get(entry); + match_files_ctx nc; + val result = vmf(c, &nc); + + if (result == next_spec_k) { + c = nc; if ((c.spec = rest(c.spec)) == nil) break; - goto repeat_spec_same_data; + } else if (result == decline_k) { + /* go on to other processing below */ + } else { + return result; } - } else if (sym == defex_s) { - val types = rest(first_spec); - if (!all_satisfy(types, func_n1(symbolp), nil)) - sem_error(spec_linenum, lit("defex arguments must all be symbols"), - nao); - (void) reduce_left(func_n2(uw_register_subtype), types, nil, nil); - if ((c.spec = rest(c.spec)) == nil) - break; - goto repeat_spec_same_data; - } else if (sym == throw_s) { - val type = second(first_spec); - val args = rest(rest(first_spec)); - if (!symbolp(type)) - sem_error(spec_linenum, lit("throw: ~a is not a type symbol"), - type, nao); - { - val values = mapcar(curry_123_2(func_n3(eval_form), - spec_linenum, c.bindings), args); - uw_throw(type, values); - } - } else if (sym == deffilter_s) { - val sym = second(first_spec); - val table = rest(rest(first_spec)); - - if (!symbolp(sym)) - sem_error(spec_linenum, lit("deffilter: ~a is not a symbol"), - first(first_spec), nao); - - if (!all_satisfy(table, andf(func_n1(listp), - chain(func_n1(length), - curry_12_2(func_n2(eq), two), - nao), - chain(func_n1(first), - func_n1(stringp), - nao), - chain(func_n1(second), - func_n1(stringp), - nao), - nao), - nil)) - sem_error(spec_linenum, - lit("deffilter arguments must be string pairs"), - nao); - register_filter(sym, table); - /* TODO: warn about replaced filter. */ - if ((c.spec = rest(c.spec)) == nil) - break; - goto repeat_spec_same_data; - } else if (sym == eof_s) { - if (c.data) { - debugf(lit("eof failed to match at ~a"), c.data_lineno, nao); - return nil; - } - if ((c.spec = rest(c.spec)) == nil) - break; - goto repeat_spec_same_data; } else { val func = uw_get_func(sym); @@ -2480,6 +2522,29 @@ static void dir_tables_init(void) sethash(v_directive_table, trailer_s, cptr((mem_t *) v_trailer)); sethash(v_directive_table, freeform_s, cptr((mem_t *) v_freeform)); sethash(v_directive_table, block_s, cptr((mem_t *) v_block)); + sethash(v_directive_table, accept_s, cptr((mem_t *) v_accept_fail)); + sethash(v_directive_table, fail_s, cptr((mem_t *) v_accept_fail)); + sethash(v_directive_table, next_s, cptr((mem_t *) v_next)); + sethash(v_directive_table, some_s, cptr((mem_t *) v_parallel)); + sethash(v_directive_table, all_s, cptr((mem_t *) v_parallel)); + sethash(v_directive_table, none_s, cptr((mem_t *) v_parallel)); + sethash(v_directive_table, maybe_s, cptr((mem_t *) v_parallel)); + sethash(v_directive_table, cases_s, cptr((mem_t *) v_parallel)); + sethash(v_directive_table, choose_s, cptr((mem_t *) v_parallel)); + sethash(v_directive_table, collect_s, cptr((mem_t *) v_collect)); + sethash(v_directive_table, flatten_s, cptr((mem_t *) v_flatten)); + sethash(v_directive_table, forget_s, cptr((mem_t *) v_forget_local)); + sethash(v_directive_table, local_s, cptr((mem_t *) v_forget_local)); + sethash(v_directive_table, merge_s, cptr((mem_t *) v_merge)); + sethash(v_directive_table, bind_s, cptr((mem_t *) v_bind)); + sethash(v_directive_table, cat_s, cptr((mem_t *) v_cat)); + sethash(v_directive_table, output_s, cptr((mem_t *) v_output)); + sethash(v_directive_table, define_s, cptr((mem_t *) v_define)); + sethash(v_directive_table, try_s, cptr((mem_t *) v_try)); + sethash(v_directive_table, defex_s, cptr((mem_t *) v_defex)); + sethash(v_directive_table, throw_s, cptr((mem_t *) v_throw)); + sethash(v_directive_table, deffilter_s, cptr((mem_t *) v_deffilter)); + sethash(v_directive_table, eof_s, cptr((mem_t *) v_eof)); } void match_init(void) |