diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | match.c | 69 | ||||
-rw-r--r-- | stdlib/doc-syms.tl | 1 | ||||
-rw-r--r-- | tests/010/eof-status.expected | 2 | ||||
-rw-r--r-- | tests/010/eof-status.txr | 3 | ||||
-rw-r--r-- | txr.1 | 60 |
6 files changed, 114 insertions, 22 deletions
@@ -418,6 +418,7 @@ tst/tests/009/json.ok: TXR_ARGS := $(addprefix tests/009/,webapp.json pass1.json tst/tests/010/align-columns.ok: TXR_ARGS := tests/010/align-columns.dat tst/tests/010/block.ok: TXR_OPTS := -B tst/tests/010/reghash.ok: TXR_OPTS := -B +tst/tests/010/eof-status.ok: TXR_OPTS := -B tst/tests/013/maze.ok: TXR_ARGS := 20 20 tst/tests/018/chmod.ok: TXR_ARGS := tst/tests/018/tempfile @@ -454,11 +454,11 @@ static val vars_to_bindings(val spec, val vars, val bindings) } typedef struct { - val bindings, specline, dataline, base, pos, data, data_lineno, file; + val bindings, specline, dataline, base, pos, data, data_lineno, file, stream; } match_line_ctx; static match_line_ctx ml_all(val bindings, val specline, val dataline, val pos, - val data, val data_lineno, val file) + val data, val data_lineno, val file, val stream) { match_line_ctx c; c.bindings = bindings; @@ -469,6 +469,7 @@ static match_line_ctx ml_all(val bindings, val specline, val dataline, val pos, c.data = data; c.data_lineno = data_lineno; c.file = file; + c.stream = stream; return c; } @@ -1485,11 +1486,11 @@ static val h_chr(match_line_ctx *c) } typedef struct { - val spec, files, curfile, bindings, data, data_lineno; + val spec, files, curfile, stream, bindings, data, data_lineno; } match_files_ctx; static match_files_ctx mf_all(val spec, val files, val bindings, val data, - val curfile); + val curfile, val stream); static val v_fun(match_files_ctx *c); @@ -1513,7 +1514,7 @@ static val h_call(match_line_ctx *c) if (ret == decline_k) { val spec = cons(new_specline, nil); - match_files_ctx vc = mf_all(spec, nil, c->bindings, nil, c->file); + match_files_ctx vc = mf_all(spec, nil, c->bindings, nil, c->file, c->stream); val vresult = v_fun(&vc); if (vresult == next_spec_k) { @@ -1585,7 +1586,7 @@ static val do_match_line(match_line_ctx *c) } else if (result == decline_k) { val spec = rlcp(cons(cons(elem, nil), nil), elem); match_files_ctx vc = mf_all(spec, nil, c->bindings, - nil, c->file); + nil, c->file, c->stream); val vresult = v_fun(&vc); if (vresult == next_spec_k) { @@ -2233,12 +2234,13 @@ void do_output(val bindings, val specs, val filter, val out) } static match_files_ctx mf_all(val spec, val files, val bindings, - val data, val curfile) + val data, val curfile, val stream) { match_files_ctx c; c.spec = spec; c.files = files; c.curfile = curfile; + c.stream = stream; c.bindings = bindings; c.data = data; c.data_lineno = if3(data, one, zero); @@ -2279,11 +2281,12 @@ static match_files_ctx mf_spec_bindings(match_files_ctx c, val spec, } static match_files_ctx mf_file_data(match_files_ctx c, val file, - val data, val data_lineno) + val stream, val data, val data_lineno) { match_files_ctx nc = c; nc.files = cons(file, c.files); nc.curfile = file; + nc.stream = stream; nc.data = data; nc.data_lineno = data_lineno; return nc; @@ -2296,6 +2299,7 @@ static match_files_ctx mf_from_ml(match_line_ctx ml) mf.spec = cons(ml.specline, nil); mf.files = nil; mf.curfile = ml.file; + mf.stream = ml.stream; mf.bindings = ml.bindings; mf.data = nil; mf.data_lineno = ml.data_lineno; @@ -2556,7 +2560,7 @@ val freeform_prepare(val vals, match_files_ctx *c, match_line_ctx *mlc) if2(stringp(second(vals)), second(vals))); val dataline = lazy_str(c->data, term, limit); *mlc = ml_all(c->bindings, first_spec, dataline, zero, - c->data, c->data_lineno, c->curfile); + c->data, c->data_lineno, c->curfile, c->stream); return limit; } @@ -2684,7 +2688,7 @@ static val v_next_impl(match_files_ctx *c) sem_error(specline, lit("(next :env) takes no additional arguments"), nao); } else { cons_bind (new_bindings, success, - match_files(mf_file_data(*c, lit("env"), env(), one))); + match_files(mf_file_data(*c, lit("env"), nil, env(), one))); if (success) return cons(new_bindings, @@ -2697,7 +2701,8 @@ static val v_next_impl(match_files_ctx *c) meta = t; } else if (!source) { cons_bind (new_bindings, success, - match_files(mf_all(c->spec, nil, c->bindings, nil, lit("empty")))); + match_files(mf_all(c->spec, nil, c->bindings, nil, + lit("empty"), nil))); if (success) return cons(new_bindings, @@ -2757,7 +2762,7 @@ static val v_next_impl(match_files_ctx *c) { cons_bind (new_bindings, success, - match_files(mf_file_data(*c, lit("var"), + match_files(mf_file_data(*c, lit("var"), nil, lazy_flatten(cdr(existing)), one))); if (success) @@ -2775,7 +2780,7 @@ static val v_next_impl(match_files_ctx *c) { cons_bind (new_bindings, success, - match_files(mf_file_data(*c, lit("var"), + match_files(mf_file_data(*c, lit("var"), nil, lazy_flatten(list_val), one))); if (success) @@ -2786,7 +2791,7 @@ static val v_next_impl(match_files_ctx *c) } else if (tlist_p) { val list_val = txeval(specline, tlist_expr, c->bindings); cons_bind (new_bindings, success, - match_files(mf_file_data(*c, lit("var"), + match_files(mf_file_data(*c, lit("var"), nil, lazy_flatten(list_val), one))); if (success) @@ -2804,7 +2809,7 @@ static val v_next_impl(match_files_ctx *c) { cons_bind (new_bindings, success, - match_files(mf_file_data(*c, lit("var"), + match_files(mf_file_data(*c, lit("var"), nil, split_str(str_val, lit("\n")), one))); if (success) @@ -2837,7 +2842,7 @@ static val v_next_impl(match_files_ctx *c) if (stream) { cons_bind (new_bindings, success, - match_files(mf_file_data(*c, str, + match_files(mf_file_data(*c, str, stream, lazy_stream_cons(stream), one))); if (success) @@ -4159,7 +4164,26 @@ static val v_eof(match_files_ctx *c) if (c->data && car(c->data)) { debuglf(c->spec, lit("eof failed to match at ~d"), c->data_lineno, nao); return nil; + } else { + spec_bind (specline, first_spec, c->spec); + val args = rest(first_spec); + + if (rest(args)) + sem_error(specline, lit("eof directive takes takes at most one argument"), nao); + + if (args) { + val pat = car(args); + val close_status = if3(streamp(c->stream), close_stream(c->stream, t), t); + + c->bindings = dest_bind(specline, c->bindings, pat, close_status, eql_f); + + if (c->bindings == t) { + debuglf(specline, lit("line mismatch (line ~d vs. ~s)"), c->data_lineno, pat, nao); + return nil; + } + } } + return next_spec_k; } @@ -4602,6 +4626,7 @@ static void open_data_source(match_files_ctx *c) c->files = cons(name, cdr(c->files)); /* Get rid of cons and nothrow */ c->curfile = source_spec; + c->stream = stream; if ((c->data = lazy_stream_cons(stream)) != nil) c->data_lineno = one; @@ -4685,7 +4710,8 @@ repeat_spec_same_data: cons_bind (new_bindings, success, match_line_completely(ml_all(c.bindings, specline, dataline, zero, - c.data, c.data_lineno, c.curfile))); + c.data, c.data_lineno, + c.curfile, c.stream))); if (!success) return nil; @@ -4712,7 +4738,7 @@ val match_filter(val name, val arg, val other_args) val spec = cons(list(cons(name, cons(in_arg_sym, cons(out_arg_sym, other_args))), nao), nil); - match_files_ctx c = mf_all(spec, nil, bindings, nil, nil); + match_files_ctx c = mf_all(spec, nil, bindings, nil, nil, nil); val ret = v_fun(&c); (void) first_spec; @@ -4750,7 +4776,8 @@ val match_fun(val name, val args, val input_in, val files_in) lazy_stream_cons(input), input); /* TODO: pass through source location context */ - match_files_ctx c = mf_all(spec, files, in_bindings, data, curfile); + match_files_ctx c = mf_all(spec, files, in_bindings, data, + curfile, if2(streamp(input), input)); val ret; ret = v_fun(&c); @@ -4767,14 +4794,14 @@ val match_fun(val name, val args, val input_in, val files_in) val include(val specline) { val spec = cons(specline, nil); - match_files_ctx c = mf_all(spec, nil, nil, nil, nil); + match_files_ctx c = mf_all(spec, nil, nil, nil, nil, nil); return v_load(&c); } val extract(val spec, val files, val predefined_bindings) { val result = match_files(mf_all(spec, files, predefined_bindings, - t, nil)); + t, nil, nil)); cons_bind (bindings, success, result); if (opt_print_bindings) { diff --git a/stdlib/doc-syms.tl b/stdlib/doc-syms.tl index b01264f2..c586cc62 100644 --- a/stdlib/doc-syms.tl +++ b/stdlib/doc-syms.tl @@ -618,6 +618,7 @@ ("env-vbind" "N-03389BE3") ("env-vbindings" "N-0018DCDC") ("enxio" "N-036B1BDB") + ("eof" "N-0336501B") ("eopnotsupp" "N-036B1BDB") ("eoverflow" "N-036B1BDB") ("eownerdead" "N-036B1BDB") diff --git a/tests/010/eof-status.expected b/tests/010/eof-status.expected new file mode 100644 index 00000000..2b636133 --- /dev/null +++ b/tests/010/eof-status.expected @@ -0,0 +1,2 @@ +a="a" +status="5" diff --git a/tests/010/eof-status.txr b/tests/010/eof-status.txr new file mode 100644 index 00000000..0da9c633 --- /dev/null +++ b/tests/010/eof-status.txr @@ -0,0 +1,3 @@ +@(next (open-command "echo a; exit 5")) +@a +@(eof status) @@ -3582,7 +3582,7 @@ A summary of the available directives follows: .coIP @(eof) Explicitly match the end of file. Fails if unmatched data remains in -the input stream. +the input stream. Can capture or match the termination status of a pipe. .coIP @(eol) Explicitly match the end of line. Fails if the current position is not the @@ -5008,6 +5008,64 @@ lines: @(do (tprint remainder)) .brev +.dir eof + +The +.code eof +directive, if not given any argument, matches successfully when no more input +is available from the current input source. + +In the following example, the +.meta line +variable captures the text +.str "One-line file" +and then since that is the last line of input, the +.code eof +directive matches: + +.IP code: +.mono +\ @line + @(eof) +.onom + +.IP data: +.mono +\ One-line file +.onom +.PP + +If the data consisted of two or more lines, +.code eof +would fail. + +The +.code eof +directive may be given a single argument, which is a pattern that matches the +termination status of the input source. This is useful when the input source +is a process pipe. For the purposes of +.codn eof , +sources which are not process pipes have the symbol +.code t +as their termination status. + +In the following example, which assumes the availability of a POSIX shell +command interpreter in the host system, the variable +.meta a +captures the string +.str a +and the +.meta status +variable captures the integer value +.codn 5 , +which is the termination status of the command: + +.verb + @(next (open-command "echo a; exit 5")) + @a + @(eof status) +.brev + .dirs some all none maybe cases choose These directives, called the parallel directives, combine multiple subqueries, |