From 023d8d8b040d5fce821080b016b4ce25eb67550d Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 26 Oct 2016 21:22:10 +0300 Subject: Add support of merge redirect to testscript parser --- build2/test/script/lexer.cxx | 3 +- build2/test/script/parser.cxx | 59 +++++++++++++++++++-- build2/test/script/script | 2 + build2/test/script/script.cxx | 17 ++++-- build2/test/script/token | 1 + build2/test/script/token.cxx | 1 + unit-tests/test/script/lexer/command-line.test | 12 +++++ unit-tests/test/script/lexer/script-line.test | 8 +++ unit-tests/test/script/parser/redirect.test | 71 ++++++++++++++++++++++++++ 9 files changed, 163 insertions(+), 11 deletions(-) diff --git a/build2/test/script/lexer.cxx b/build2/test/script/lexer.cxx index 98d52e7..003e4ac 100644 --- a/build2/test/script/lexer.cxx +++ b/build2/test/script/lexer.cxx @@ -288,7 +288,7 @@ namespace build2 { xchar p (peek ()); - if (p == '+' || p == '-' || p == ':' || p == '>') + if (p == '+' || p == '-' || p == '&' || p == ':' || p == '>') { get (); @@ -296,6 +296,7 @@ namespace build2 { case '+': return make_token (type::out_pass); case '-': return make_token (type::out_null); + case '&': return make_token (type::out_merge); case ':': return make_token (type::out_str_nn); case '>': { diff --git a/build2/test/script/parser.cxx b/build2/test/script/parser.cxx index ae8df12..70f1ef2 100644 --- a/build2/test/script/parser.cxx +++ b/build2/test/script/parser.cxx @@ -564,9 +564,11 @@ namespace build2 in_string, in_document, in_file, + out_merge, out_string, out_document, out_file, + err_merge, err_string, err_document, err_file @@ -592,6 +594,24 @@ namespace build2 auto add_word = [&c, &p, &nn, &app, &hd, this] (string&& w, const location& l) { + auto add_merge = [&l, this] (redirect& r, const string& w, int fd) + { + try + { + size_t n; + if (stoi (w, &n) != fd || n != w.size ()) + throw invalid_argument (string ()); + } + catch (const exception&) + { + fail (l) << "invalid " << (fd == 1 ? "stderr" : "stdout") + << " merge redirect file descriptor '" << w << "'" << + info << "must be " << fd; + } + + r.fd = fd; + }; + auto add_here_str = [&nn] (redirect& r, string&& w) { if (!nn) w += '\n'; @@ -642,14 +662,17 @@ namespace build2 break; } - case pending::in_document: add_here_end (c.in, move (w)); break; - case pending::out_document: add_here_end (c.out, move (w)); break; - case pending::err_document: add_here_end (c.err, move (w)); break; + case pending::out_merge: add_merge (c.out, w, 2); break; + case pending::err_merge: add_merge (c.err, w, 1); break; case pending::in_string: add_here_str (c.in, move (w)); break; case pending::out_string: add_here_str (c.out, move (w)); break; case pending::err_string: add_here_str (c.err, move (w)); break; + case pending::in_document: add_here_end (c.in, move (w)); break; + case pending::out_document: add_here_end (c.out, move (w)); break; + case pending::err_document: add_here_end (c.err, move (w)); break; + case pending::in_file: add_file (c.in, "stdin", move (w)); break; case pending::out_file: add_file (c.out, "stdout", move (w)); break; case pending::err_file: add_file (c.err, "stderr", move (w)); break; @@ -673,9 +696,11 @@ namespace build2 case pending::in_string: what = "stdin here-string"; break; case pending::in_document: what = "stdin here-document end"; break; case pending::in_file: what = "stdin file"; break; + case pending::out_merge: what = "stdout file descriptor"; break; case pending::out_string: what = "stdout here-string"; break; case pending::out_document: what = "stdout here-document end"; break; case pending::out_file: what = "stdout file"; break; + case pending::err_merge: what = "stderr file descriptor"; break; case pending::err_string: what = "stderr here-string"; break; case pending::err_document: what = "stderr here-document end"; break; case pending::err_file: what = "stderr file"; break; @@ -741,6 +766,7 @@ namespace build2 } case type::out_pass: case type::out_null: + case type::out_merge: case type::out_str: case type::out_str_nn: case type::out_doc: @@ -764,6 +790,8 @@ namespace build2 case type::in_null: case type::out_null: rt = redirect_type::null; break; + case type::out_merge: rt = redirect_type::merge; break; + case type::in_str_nn: case type::out_str_nn: nn = true; // Fall through. case type::in_str: @@ -788,6 +816,14 @@ namespace build2 case redirect_type::pass: case redirect_type::null: break; + case redirect_type::merge: + switch (fd) + { + case 0: assert (false); break; + case 1: p = pending::out_merge; break; + case 2: p = pending::err_merge; break; + } + break; case redirect_type::here_string: switch (fd) { @@ -842,6 +878,8 @@ namespace build2 case type::in_null: case type::out_null: + case type::out_merge: + case type::in_str: case type::in_doc: case type::out_str: @@ -904,6 +942,8 @@ namespace build2 case type::in_null: case type::out_null: + case type::out_merge: + case type::in_str: case type::in_doc: case type::out_str: @@ -1050,6 +1090,8 @@ namespace build2 case type::in_null: case type::out_null: + case type::out_merge: + case type::in_str: case type::out_str: @@ -1089,11 +1131,18 @@ namespace build2 } } - // Verify we don't have anything pending to be filled. - // if (!pre_parse_) + { + // Verify we don't have anything pending to be filled. + // check_pending (l); + if (c.out.type == redirect_type::merge && + c.err.type == redirect_type::merge) + fail (l) << "stdout and stderr merge redirects" << + info << "should not be specified at the same time"; + } + // While we no longer need to recognize command line operators, we // also don't expect a valid test trailer to contain them. So we are // going to continue lexing in the script_line mode. diff --git a/build2/test/script/script b/build2/test/script/script index 574760e..f0d8f21 100644 --- a/build2/test/script/script +++ b/build2/test/script/script @@ -43,6 +43,7 @@ namespace build2 none, pass, null, + merge, here_string, here_document, file @@ -67,6 +68,7 @@ namespace build2 union { + int fd; // Used with 'merge' type. string str; // Note: includes trailing newline, if required. doc_type doc; file_type file; diff --git a/build2/test/script/script.cxx b/build2/test/script/script.cxx index ac11546..8de023f 100644 --- a/build2/test/script/script.cxx +++ b/build2/test/script/script.cxx @@ -43,9 +43,10 @@ namespace build2 switch (r.type) { - case redirect_type::none: assert (false); break; - case redirect_type::pass: o << '+'; break; - case redirect_type::null: o << '-'; break; + case redirect_type::none: assert (false); break; + case redirect_type::pass: o << '+'; break; + case redirect_type::null: o << '-'; break; + case redirect_type::merge: o << '&' << r.fd; break; case redirect_type::here_string: { @@ -140,7 +141,8 @@ namespace build2 { case redirect_type::none: case redirect_type::pass: - case redirect_type::null: break; + case redirect_type::null: + case redirect_type::merge: break; case redirect_type::here_string: new (&str) string (); break; case redirect_type::here_document: new (&doc) doc_type (); break; @@ -158,6 +160,8 @@ namespace build2 case redirect_type::pass: case redirect_type::null: break; + case redirect_type::merge: fd = r.fd; break; + case redirect_type::here_string: { new (&str) string (move (r.str)); @@ -186,6 +190,8 @@ namespace build2 case redirect_type::pass: case redirect_type::null: break; + case redirect_type::merge: fd = r.fd; break; + case redirect_type::here_string: new (&str) string (r.str); break; case redirect_type::here_document: new (&doc) doc_type (r.doc); break; case redirect_type::file: @@ -203,7 +209,8 @@ namespace build2 { case redirect_type::none: case redirect_type::pass: - case redirect_type::null: break; + case redirect_type::null: + case redirect_type::merge: break; case redirect_type::here_string: str.~string (); break; case redirect_type::here_document: doc.~doc_type (); break; diff --git a/build2/test/script/token b/build2/test/script/token index d22b758..a1374fe 100644 --- a/build2/test/script/token +++ b/build2/test/script/token @@ -44,6 +44,7 @@ namespace build2 out_pass, // >+ out_null, // >- + out_merge, // >& out_str, // > out_str_nn, // >: out_doc, // >> diff --git a/build2/test/script/token.cxx b/build2/test/script/token.cxx index 376a92e..1cd0859 100644 --- a/build2/test/script/token.cxx +++ b/build2/test/script/token.cxx @@ -41,6 +41,7 @@ namespace build2 case token_type::out_pass: os << q << ">+" << q; break; case token_type::out_null: os << q << ">-" << q; break; + case token_type::out_merge: os << q << ">&" << q; break; case token_type::out_str: os << q << '>' << q; break; case token_type::out_str_nn: os << q << ">:" << q; break; case token_type::out_doc: os << q << ">>" << q; break; diff --git a/unit-tests/test/script/lexer/command-line.test b/unit-tests/test/script/lexer/command-line.test index 765b59a..4a71d46 100644 --- a/unit-tests/test/script/lexer/command-line.test +++ b/unit-tests/test/script/lexer/command-line.test @@ -40,6 +40,18 @@ $* <:"1 >-" >>EOO # arg-out-null-redirect >- EOO +$* <:"1>&2" >>EOO # merge-redirect +'1' +>& +'2' +EOO + +$* <:"1 >&2" >>EOO # arg-merge-redirect +'1 ' +>& +'2' +EOO + $* <:"0>EOO # in-str-redirect '0' < diff --git a/unit-tests/test/script/lexer/script-line.test b/unit-tests/test/script/lexer/script-line.test index a217591..36d440c 100644 --- a/unit-tests/test/script/lexer/script-line.test +++ b/unit-tests/test/script/lexer/script-line.test @@ -33,6 +33,14 @@ $* <"cmd <- 1>-" >>EOO # null-redirect EOO +$* <"cmd 1>&2" >>EOO # merge-redirect +'cmd' +'1' +>& +'2' + +EOO + $* <"cmd b" >>EOO # str-redirect 'cmd' < diff --git a/unit-tests/test/script/parser/redirect.test b/unit-tests/test/script/parser/redirect.test index 83b40cd..3497fd9 100644 --- a/unit-tests/test/script/parser/redirect.test +++ b/unit-tests/test/script/parser/redirect.test @@ -3,3 +3,74 @@ cmd 0<<>>b 2>>>&c EOI cmd <<>>b 2>>>&c EOO + +$* <>EOO # out-merge1 +cmd 1>&2 +EOI +cmd >&2 +EOO + +$* <>EOO # out-merge2 +cmd 1>&2 2>&1 2>a +EOI +cmd >&2 2>a +EOO + +$* <>EOE != 0 # out-merge-fail1 +cmd 1>&a +EOI +testscript:1:8: error: invalid stdout merge redirect file descriptor 'a' + info: must be 2 +EOE + +$* <>EOE != 0 # out-merge-fail2 +cmd 1>&1 +EOI +testscript:1:8: error: invalid stdout merge redirect file descriptor '1' + info: must be 2 +EOE + +$* <>EOE != 0 # out-merge-fail3 +cmd 1>& +EOI +testscript:1:8: error: missing stdout file descriptor +EOE + +$* <>EOO # err-merge1 +cmd 2>&1 +EOI +cmd 2>&1 +EOO + +$* <>EOO # err-merge2 +cmd 1>&2 2>&1 >a +EOI +cmd >a 2>&1 +EOO + +$* <>EOE != 0 # err-merge-fail1 +cmd 2>&2 +EOI +testscript:1:8: error: invalid stderr merge redirect file descriptor '2' + info: must be 1 +EOE + +$* <>EOE != 0 # err-merge-fail2 +cmd 2>&2 +EOI +testscript:1:8: error: invalid stderr merge redirect file descriptor '2' + info: must be 1 +EOE + +$* <>EOE != 0 # err-merge-fail3 +cmd 2>& +EOI +testscript:1:8: error: missing stderr file descriptor +EOE + +$* <>EOE != 0 # merge-fail1 +cmd 1>&2 2>&1 +EOI +testscript:1:14: error: stdout and stderr merge redirects + info: should not be specified both +EOE -- cgit v1.1