summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog28
-rw-r--r--lib.c26
-rw-r--r--lib.h3
-rw-r--r--match.c111
-rw-r--r--stream.c90
-rw-r--r--stream.h2
6 files changed, 241 insertions, 19 deletions
diff --git a/ChangeLog b/ChangeLog
index f77390ac..22e0bfb3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
diff --git a/lib.c b/lib.c
index 91e958b8..c773ca60 100644
--- a/lib.c
+++ b/lib.c
@@ -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)
diff --git a/lib.h b/lib.h
index dca8220c..ab2e61d5 100644
--- a/lib.h
+++ b/lib.h
@@ -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);
diff --git a/match.c b/match.c
index 733b99cf..f5e7c1c8 100644
--- a/match.c
+++ b/match.c
@@ -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)
diff --git a/stream.c b/stream.c
index aea2960d..bb9641df 100644
--- a/stream.c
+++ b/stream.c
@@ -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;
diff --git a/stream.h b/stream.h
index 1864422d..9e703378 100644
--- a/stream.h
+++ b/stream.h
@@ -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);