diff options
-rw-r--r-- | ChangeLog | 28 | ||||
-rw-r--r-- | lib.c | 26 | ||||
-rw-r--r-- | lib.h | 3 | ||||
-rw-r--r-- | match.c | 111 | ||||
-rw-r--r-- | stream.c | 90 | ||||
-rw-r--r-- | stream.h | 2 |
6 files changed, 241 insertions, 19 deletions
@@ -1,5 +1,33 @@ 2011-10-21 Kaz Kylheku <kaz@kylheku.com> + New features. Strling list output streams in stream + library, allow output to be captured as a list of strings + representing lines (in contrast to string streams which capture + a single string). + + The output directive can output to a variable, + and next can scan over a variable. + + * lib.c (span_str, compl_span_str, break_str): New functions. + + * lib.h (span_str, compl_span_str, break_str): New functions declared. + + * match.c (into_k, var_k): New keyword variables. + (mf_file_data): New static function. + (v_next): Refactored argument handling. Added support for :var + keyword. + (v_output): Added support for :into keyword. + + * stream.c (strlist_mark, strlist_out_put_string, + strlist_out_put_char): New static functions. + (strlist_out_ops): New static struct. + (make_strlist_output_stream, get_list_from_stream): New functions. + + * stream.h (make_strlist_output_stream, get_list_from_stream): New + functions declared. + +2011-10-21 Kaz Kylheku <kaz@kylheku.com> + * lib.c (proper_plist_to_alist, improper_plist_to_alist): New functions. @@ -1199,6 +1199,32 @@ val chr_str_set(val str, val index, val chr) return chr; } +val span_str(val str, val set) +{ + const wchar_t *cstr = c_str(str); + const wchar_t *cset = c_str(set); + size_t span = wcsspn(cstr, cset); + return num(span); +} + +val compl_span_str(val str, val set) +{ + const wchar_t *cstr = c_str(str); + const wchar_t *cset = c_str(set); + size_t span = wcscspn(cstr, cset); + return num(span); +} + +val break_str(val str, val set) +{ + const wchar_t *cstr = c_str(str); + const wchar_t *cset = c_str(set); + const wchar_t *brk = wcspbrk(cstr, cset); + if (!brk) + return nil; + return num(brk - cstr); +} + val symbol_name(val sym) { if (sym) @@ -353,6 +353,9 @@ val chrp(val chr); wchar_t c_chr(val chr); val chr_str(val str, val index); val chr_str_set(val str, val index, val chr); +val span_str(val str, val set); +val compl_span_str(val str, val set); +val break_str(val str, val set); val sym_name(val sym); val make_sym(val name); val make_package(val name); @@ -53,7 +53,7 @@ val mingap_k, maxgap_k, gap_k, mintimes_k, maxtimes_k, times_k; val lines_k, chars_k; val choose_s, longest_k, shortest_k, greedy_k; val vars_k; -val append_k; +val append_k, into_k, var_k; static val h_directive_table, v_directive_table; @@ -1388,6 +1388,16 @@ static match_files_ctx mf_spec_bindings(match_files_ctx c, val spec, return nc; } +static match_files_ctx mf_file_data(match_files_ctx c, val file, + val data, val data_lineno) +{ + match_files_ctx nc = c; + nc.files = cons(file, c.files); + nc.data = data; + nc.data_lineno = data_lineno; + return nc; +} + static val match_files(match_files_ctx a); typedef val (*v_match_func)(match_files_ctx c, match_files_ctx *cout); @@ -1604,30 +1614,60 @@ static val v_next(match_files_ctx c, match_files_ctx *cout) 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))); + val args = rest(first_spec); + val source = first(args); - if (success) - return cons(new_bindings, - if3(c.data, cons(c.data, c.data_lineno), t)); - return nil; - } - arg = second(source); + if (source == args_k) { + if (rest(args)) + sem_error(spec_linenum, lit("(next :args) takes no additional arguments"), nao); + 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; + } + + if (keywordp(first(args))) { + source = nil; + } else { + pop(&args); } + if (args && !keywordp(first(args))) + sem_error(spec_linenum, lit("next: keyword argument expected, not ~s"), first(args), nao); + { - val eval = eval_form(spec_linenum, arg, c.bindings); + val alist = improper_plist_to_alist(args, list(nothrow_k, nao)); + val from_var = cdr(assoc(alist, var_k)); + val nothrow = cdr(assoc(alist, nothrow_k)); + val eval = eval_form(spec_linenum, source, c.bindings); val str = cdr(eval); - if (eq(second(source), nothrow_k)) { + if (!from_var && !source) + sem_error(spec_linenum, lit("next: source required before keyword arguments"), nao); + + if (from_var) { + val existing = assoc(c.bindings, from_var); + + if (!symbolp(from_var)) + sem_error(spec_linenum, lit(":var requires a variable, not ~s"), from_var, nao); + + if (!existing) + sem_error(spec_linenum, lit(":var specifies unbound variable ~s"), from_var, nao); + + { + cons_bind (new_bindings, success, + match_files(mf_file_data(c, lit("var"), + flatten(cdr(existing)), num(1)))); + + if (success) + return cons(new_bindings, + if3(c.data, cons(c.data, c.data_lineno), t)); + return nil; + } + } else if (nothrow) { if (str) { c.files = cons(cons(nothrow_k, str), c.files); } else { @@ -2122,6 +2162,37 @@ static val v_output(match_files_ctx c, match_files_ctx *cout) } } + { + val into_var = cdr(assoc(alist, into_k)); + + if (into_var) { + val stream = make_strlist_output_stream(); + + if (!symbolp(into_var)) + sem_error(spec_linenum, lit(":into requires a variable, not ~s"), into_var, nao); + + debugf(lit("opening string list stream"), nao); + do_output(c.bindings, specs, filter, stream); + + { + val existing = assoc(c.bindings, into_var); + val list_out = get_list_from_stream(stream); + + if (existing) { + if (append) { + *cdr_l(existing) = append2(cdr(existing), list_out); + } else { + *cdr_l(existing) = list_out; + } + } else { + c.bindings = acons(c.bindings, into_var, list_out); + } + } + *cout = c; + return next_spec_k; + } + } + fp = (errno = 0, complex_open(dest, t, append)); debugf(lit("opening data sink ~a"), dest, nao); @@ -2591,6 +2662,8 @@ static void syms_init(void) greedy_k = intern(lit("greedy"), keyword_package); vars_k = intern(lit("vars"), keyword_package); append_k = intern(lit("append"), keyword_package); + into_k = intern(lit("into"), keyword_package); + var_k = intern(lit("var"), keyword_package); } static void dir_tables_init(void) @@ -438,6 +438,96 @@ static struct strm_ops string_out_ops = { 0, }; +static void strlist_mark(val stream) +{ + val stuff = (val) stream->co.handle; + gc_mark(stuff); +} + +static val strlist_out_put_string(val stream, val str) +{ + val cell = (val) stream->co.handle; + cons_bind (lines, strstream, cell); + + for (;;) { + val length = length_str(str); + val span_to_newline = compl_span_str(str, lit("\n")); + + if (zerop(length)) + break; + + put_string(strstream, sub_str(str, nil, span_to_newline)); + + if (equal(span_to_newline, length)) + break; + + str = sub_str(str, plus(span_to_newline, num(1)), nil); + push(get_string_from_stream(strstream), &lines); + strstream = make_string_output_stream(); + } + + *car_l(cell) = lines; + *cdr_l(cell) = strstream; + + return t; +} + +static val strlist_out_put_char(val stream, val ch) +{ + val cell = (val) stream->co.handle; + cons_bind (lines, strstream, cell); + + if (ch == chr('\n')) { + push(get_string_from_stream(strstream), &lines); + strstream = make_string_output_stream(); + } else { + put_char(strstream, ch); + } + + *car_l(cell) = lines; + *cdr_l(cell) = strstream; + + return t; +} + +static struct strm_ops strlist_out_ops = { + { cobj_equal_op, + cobj_print_op, + cobj_destroy_stub_op, + strlist_mark, + cobj_hash_op }, + strlist_out_put_string, + strlist_out_put_char, + 0, + 0, + 0, + 0, +}; + +val make_strlist_output_stream(void) +{ + return cobj((mem_t *) cons(nil, make_string_output_stream()), + stream_s, &strlist_out_ops.cobj_ops); +} + +val get_list_from_stream(val stream) +{ + type_check (stream, COBJ); + type_assert (stream->co.cls == stream_s, + (lit("~a is not a stream"), stream, nao)); + + if (stream->co.ops == &strlist_out_ops.cobj_ops) { + val cell = (val) stream->co.handle; + cons_bind (lines, strstream, cell); + val stray = get_string_from_stream(strstream); + if (!zerop(length_str(stray))) + push(stray, &lines); + return nreverse(lines); + } + + type_mismatch(lit("~s is not a string list stream"), stream); +} + static val dir_get_line(val stream) { DIR *handle = (DIR *) stream->co.handle; @@ -32,6 +32,8 @@ val make_string_input_stream(val); val make_string_byte_input_stream(val); val make_string_output_stream(void); val get_string_from_stream(val); +val make_strlist_output_stream(void); +val get_list_from_stream(val); val make_dir_stream(DIR *); val close_stream(val stream, val throw_on_error); val get_line(val); |