diff options
Diffstat (limited to 'libbuild2/script')
-rw-r--r-- | libbuild2/script/lexer+command-expansion.test.testscript | 102 | ||||
-rw-r--r-- | libbuild2/script/lexer.cxx | 162 | ||||
-rw-r--r-- | libbuild2/script/lexer.hxx | 53 | ||||
-rw-r--r-- | libbuild2/script/lexer.test.cxx | 12 | ||||
-rw-r--r-- | libbuild2/script/parser.cxx | 68 | ||||
-rw-r--r-- | libbuild2/script/parser.hxx | 3 | ||||
-rw-r--r-- | libbuild2/script/run.cxx | 13 | ||||
-rw-r--r-- | libbuild2/script/script.cxx | 118 | ||||
-rw-r--r-- | libbuild2/script/script.hxx | 21 | ||||
-rw-r--r-- | libbuild2/script/token.cxx | 43 | ||||
-rw-r--r-- | libbuild2/script/token.hxx | 24 |
11 files changed, 441 insertions, 178 deletions
diff --git a/libbuild2/script/lexer+command-expansion.test.testscript b/libbuild2/script/lexer+command-expansion.test.testscript index 9422cba..f4d69d2 100644 --- a/libbuild2/script/lexer+command-expansion.test.testscript +++ b/libbuild2/script/lexer+command-expansion.test.testscript @@ -113,17 +113,37 @@ test.arguments = command-expansion { : newline : - $* <:"0<a b" >>EOO + $* <:"0<<<=a b" >>EOO '0' - < + <<<= 'a b' EOO : no-newline : - $* <:"0<:a b" >>EOO + $* <:"0<<<=:a b" >>EOO '0' - <: + <<<=: + 'a b' + EOO + } + + : in-alias + : + { + : newline + : + $* <:"0<<<a b" >>EOO + '0' + <<< + 'a b' + EOO + + : no-newline + : + $* <:"0<<<:a b" >>EOO + '0' + <<<: 'a b' EOO } @@ -133,17 +153,17 @@ test.arguments = command-expansion { : newline : - $* <:"1>a b" >>EOO + $* <:"1>>>?a b" >>EOO '1' - > + >>>? 'a b' EOO : no-newline : - $* <:"1>:a b" >>EOO + $* <:"1>>>?:a b" >>EOO '1' - >: + >>>?: 'a b' EOO } @@ -157,6 +177,26 @@ test.arguments = command-expansion { : newline : + $* <:"0<<=E O I" >>EOO + '0' + <<= + 'E O I' + EOO + + : no-newline + : + $* <:"0<<=:E O I" >>EOO + '0' + <<=: + 'E O I' + EOO + } + + : in-alias + : + { + : newline + : $* <:"0<<E O I" >>EOO '0' << @@ -177,17 +217,17 @@ test.arguments = command-expansion { : newline : - $* <:"1>>E O O" >>EOO + $* <:"1>>?E O O" >>EOO '1' - >> + >>? 'E O O' EOO : no-newline : - $* <:"1>>:E O O" >>EOO + $* <:"1>>?:E O O" >>EOO '1' - >>: + >>?: 'E O O' EOO } @@ -198,9 +238,17 @@ test.arguments = command-expansion { : in : - $* <:"0<<<a b" >>EOO + $* <:"0<=a b" >>EOO '0' - <<< + <= + 'a b' + EOO + + : in-alias + : + $* <:"0<a b" >>EOO + '0' + < 'a b' EOO @@ -212,6 +260,14 @@ test.arguments = command-expansion 'a b' EOO + : out-alias + : + $* <:"1>a b" >>EOO + '1' + > + 'a b' + EOO + : out-app : $* <:"1>+a b" >>EOO @@ -219,8 +275,26 @@ test.arguments = command-expansion >+ 'a b' EOO + + : out-app-alias + : + $* <:"1>>a b" >>EOO + '1' + >> + 'a b' + EOO } +: no-out-alias +: +$* <:"1>>>a b" >>EOO +'1' +>> +> +'a b' +EOO + + : cleanup : { diff --git a/libbuild2/script/lexer.cxx b/libbuild2/script/lexer.cxx index fff9307..d78e999 100644 --- a/libbuild2/script/lexer.cxx +++ b/libbuild2/script/lexer.cxx @@ -165,7 +165,7 @@ namespace build2 // if (m == lexer_mode::command_expansion) { - if (optional<token> t = next_cmd_op (c, sep, m)) + if (optional<token> t = next_cmd_op (c, sep)) return move (*t); } @@ -176,14 +176,12 @@ namespace build2 } optional<token> lexer:: - next_cmd_op (const xchar& c, bool sep, lexer_mode m) + next_cmd_op (const xchar& c, bool sep) { - auto make_token = [&sep, &m, &c] (type t, string v = string ()) + auto make_token = [&sep, &c] (type t, string v = string ()) { - bool q (m == lexer_mode::here_line_double); - return token (t, move (v), sep, - (q ? quote_type::double_ : quote_type::unquoted), q, + quote_type::unquoted, false, c.line, c.column, token_printer); }; @@ -247,89 +245,183 @@ namespace build2 // case '<': { - type r (type::in_str); + optional<type> r; xchar p (peek ()); - if (p == '|' || p == '-' || p == '<') + if (p == '|' || p == '-' || p == '=' || p == '<') // <| <- <= << { - get (); + xchar c (get ()); switch (p) { - case '|': return make_token (type::in_pass); - case '-': return make_token (type::in_null); - case '<': + case '|': return make_token (type::in_pass); // <| + case '-': return make_token (type::in_null); // <- + case '=': return make_token (type::in_file); // <= + case '<': // << { - r = type::in_doc; p = peek (); - if (p == '<') + if (p == '=' || p == '<') // <<= <<< { - get (); - r = type::in_file; + xchar c (get ()); + + switch (p) + { + case '=': + { + r = type::in_doc; // <<= + break; + } + case '<': + { + p = peek (); + + if (p == '=') + { + get (); + r = type::in_str; // <<<= + } + + if (!r && redirect_aliases.lll) + r = type::in_lll; // <<< + + // We can still end up with the << or < redirect alias, + // if any of them is present. + // + if (!r) + unget (c); + } + + break; + } } + + if (!r && redirect_aliases.ll) + r = type::in_ll; // << + + // We can still end up with the < redirect alias, if it is + // present. + // + if (!r) + unget (c); + break; } } } + if (!r && redirect_aliases.l) + r = type::in_l; // < + + if (!r) + return nullopt; + // Handle modifiers. // const char* mods (nullptr); - switch (r) + + switch (redirect_aliases.resolve (*r)) { case type::in_str: case type::in_doc: mods = ":/"; break; } - return make_token_with_modifiers (r, mods); + token t (make_token_with_modifiers (*r, mods)); + + return t; } // > // case '>': { - type r (type::out_str); + optional<type> r; xchar p (peek ()); - if (p == '|' || p == '-' || p == '!' || p == '&' || - p == '=' || p == '+' || p == '>') + if (p == '|' || p == '-' || p == '!' || p == '&' || // >| >- >! >& + p == '=' || p == '+' || p == '?' || p == '>') // >= >+ >? >> { - get (); + xchar c (get ()); switch (p) { - case '|': return make_token (type::out_pass); - case '-': return make_token (type::out_null); - case '!': return make_token (type::out_trace); - case '&': return make_token (type::out_merge); - case '=': return make_token (type::out_file_ovr); - case '+': return make_token (type::out_file_app); - case '>': + case '|': return make_token (type::out_pass); // >| + case '-': return make_token (type::out_null); // >- + case '!': return make_token (type::out_trace); // >! + case '&': return make_token (type::out_merge); // >& + case '=': return make_token (type::out_file_ovr); // >= + case '+': return make_token (type::out_file_app); // >+ + case '?': return make_token (type::out_file_cmp); // >? + case '>': // >> { - r = type::out_doc; p = peek (); - if (p == '>') + if (p == '?' || p == '>') // >>? >>> { - get (); - r = type::out_file_cmp; + xchar c (get ()); + + switch (p) + { + case '?': + { + r = type::out_doc; // >>? + break; + } + case '>': + { + p = peek (); + + if (p == '?') + { + get (); + r = type::out_str; // >>>? + } + + if (!r && redirect_aliases.ggg) + r = type::out_ggg; // >>> + + // We can still end up with the >> or > redirect alias, + // if any of themis present. + // + if (!r) + unget (c); + } + + break; + } } + + if (!r && redirect_aliases.gg) + r = type::out_gg; // >> + + // We can still end up with the > redirect alias, if it is + // present. + // + if (!r) + unget (c); + break; } } } + if (!r && redirect_aliases.g) + r = type::out_g; // > + + if (!r) + return nullopt; + // Handle modifiers. // const char* mods (nullptr); const char* stop (nullptr); - switch (r) + + switch (redirect_aliases.resolve (*r)) { case type::out_str: case type::out_doc: mods = ":/~"; stop = "~"; break; } - return make_token_with_modifiers (r, mods, stop); + return make_token_with_modifiers (*r, mods, stop); } } diff --git a/libbuild2/script/lexer.hxx b/libbuild2/script/lexer.hxx index c0d617d..bdeba66 100644 --- a/libbuild2/script/lexer.hxx +++ b/libbuild2/script/lexer.hxx @@ -33,21 +33,58 @@ namespace build2 lexer_mode (base_type v): base_type (v) {} }; + // Redirects the <, <<, <<<, >, >>, and >>> aliases resolve to. + // + struct redirect_aliases + { + optional<token_type> l; // < + optional<token_type> ll; // << + optional<token_type> lll; // <<< + optional<token_type> g; // > + optional<token_type> gg; // >> + optional<token_type> ggg; // >>> + + // If the token type is a redirect alias then return the token type + // it resolves to and the passed type otherwise. It's the caller's + // responsibility to make sure that the corresponding alias is present. + // + token_type + resolve (token_type t) const noexcept + { + switch (t) + { + case token_type::in_l: assert (l); return *l; + case token_type::in_ll: assert (ll); return *ll; + case token_type::in_lll: assert (lll); return *lll; + case token_type::out_g: assert (g); return *g; + case token_type::out_gg: assert (gg); return *gg; + case token_type::out_ggg: assert (ggg); return *ggg; + } + + return t; + } + }; + class lexer: public build2::lexer { public: using base_lexer = build2::lexer; using base_mode = build2::lexer_mode; - // Note that neither the name nor escape arguments are copied. + using redirect_aliases_type = script::redirect_aliases; + + // Note that none of the name, redirect aliases, and escape arguments + // are copied. // lexer (istream& is, const path_name& name, lexer_mode m, + const redirect_aliases_type& ra, const char* escapes = nullptr) : base_lexer (is, name, 1 /* line */, nullptr /* escapes */, - false /* set_mode */) + false /* set_mode */), + redirect_aliases (ra) { mode (m, '\0', escapes); } @@ -69,19 +106,23 @@ namespace build2 virtual token next () override; + public: + const redirect_aliases_type& redirect_aliases; + protected: lexer (istream& is, const path_name& name, uint64_t line, const char* escapes, - bool set_mode) - : base_lexer (is, name, line, escapes, set_mode) {} + bool set_mode, + const redirect_aliases_type& ra) + : base_lexer (is, name, line, escapes, set_mode), + redirect_aliases (ra) {} // Return the next token if it is a command operator (|, ||, &&, // redirect, or cleanup) and nullopt otherwise. // optional<token> next_cmd_op (const xchar&, // The token first character (last got char). - bool sep, // The token is separated. - lexer_mode); // The current (potentially "expired") mode. + bool sep); // The token is separated. private: token diff --git a/libbuild2/script/lexer.test.cxx b/libbuild2/script/lexer.test.cxx index 24fe335..b8de241 100644 --- a/libbuild2/script/lexer.test.cxx +++ b/libbuild2/script/lexer.test.cxx @@ -37,7 +37,17 @@ namespace build2 cin.exceptions (istream::failbit | istream::badbit); path_name in ("<stdin>"); - lexer l (cin, in, m); + + using type = token_type; + + redirect_aliases ra {type (type::in_file), + type (type::in_doc), + type (type::in_str), + type (type::out_file_ovr), + type (type::out_file_app), + nullopt}; + + lexer l (cin, in, m, ra); // No use printing eos since we will either get it or loop forever. // diff --git a/libbuild2/script/parser.cxx b/libbuild2/script/parser.cxx index 6c651a6..aa60111 100644 --- a/libbuild2/script/parser.cxx +++ b/libbuild2/script/parser.cxx @@ -97,7 +97,8 @@ namespace build2 } pair<command_expr, parser::here_docs> parser:: - parse_command_expr (token& t, type& tt) + parse_command_expr (token& t, type& tt, + const redirect_aliases& ra) { // enter: first token of the command line // leave: <newline> or unknown token @@ -201,7 +202,7 @@ namespace build2 { assert (r); // Must already be present. - if (r->modifiers.find (':') == string::npos) + if (r->modifiers ().find (':') == string::npos) w += '\n'; r->str = move (w); }; @@ -218,7 +219,7 @@ namespace build2 case 2: what = "stderr regex redirect"; break; } - check_regex_mod (r->modifiers, w, l, what); + check_regex_mod (r->modifiers (), w, l, what); regex_parts rp (parse_regex (w, l, what)); @@ -233,7 +234,7 @@ namespace build2 // Note that the position is synthetic, but that's ok as we don't // expect any diagnostics to refer this line. // - if (r->modifiers.find (':') == string::npos) + if (r->modifiers ().find (':') == string::npos) re.lines.emplace_back (l.line, l.column, string (), false); }; @@ -382,9 +383,24 @@ namespace build2 // Parse the redirect operator. // - auto parse_redirect = - [&c, &expr, &p, &mod, &hd, this] (token& t, const location& l) + // If the token type is the redirect alias then tt must contain the type + // the alias resolves to and the token type otherwise. Note that this + // argument defines the redirect semantics. Also note that the token is + // saved into the redirect to keep the modifiers and the original + // representation. + // + auto parse_redirect = [&c, &expr, &p, &mod, &hd, this] + (token&& t, type tt, const location& l) { + // The redirect alias token type must be resolved. + // + assert (tt != type::in_l && + tt != type::in_ll && + tt != type::in_lll && + tt != type::out_g && + tt != type::out_gg && + tt != type::out_ggg); + // Our semantics is the last redirect seen takes effect. // assert (p == pending::none && mod.empty ()); @@ -415,8 +431,6 @@ namespace build2 c.arguments.pop_back (); } - type tt (t.type); - // Validate/set default file descriptor. // switch (tt) @@ -452,7 +466,9 @@ namespace build2 } } - mod = move (t.value); + // Don't move as we will save the token into the redirect object. + // + mod = t.value; // Handle the none redirect (no data allowed) in the switch construct // if/when the respective syntax is invented. @@ -516,7 +532,7 @@ namespace build2 // Don't move as still may be used for pending here-document end // marker processing. // - r->modifiers = mod; + r->token = move (t); switch (rt) { @@ -640,6 +656,8 @@ namespace build2 for (bool done (false); !done; l = get_location (t)) { + tt = ra.resolve (tt); + switch (tt) { case type::newline: @@ -878,7 +896,7 @@ namespace build2 case type::out_file_ovr: case type::out_file_app: { - parse_redirect (t, l); + parse_redirect (move (t), tt, l); break; } @@ -1053,11 +1071,11 @@ namespace build2 // args = 'x=\"foo bar\"' // cmd $args # cmd x="foo bar" // - istringstream is (s); path_name in ("<string>"); lexer lex (is, in, lexer_mode::command_expansion, + ra, "\'\"\\"); // Treat the first "sub-token" as always separated from what @@ -1075,7 +1093,7 @@ namespace build2 for (; t.type != type::eos; t = lex.next ()) { - type tt (t.type); + type tt (ra.resolve (t.type)); l = build2::get_location (t, in); // Re-lexing double-quotes will recognize $, ( inside as @@ -1155,7 +1173,7 @@ namespace build2 case type::out_file_ovr: case type::out_file_app: { - parse_redirect (t, l); + parse_redirect (move (t), tt, l); break; } @@ -1309,8 +1327,17 @@ namespace build2 { command& c (p.first[i->expr].pipe[i->pipe]); - (i->fd == 0 ? c.in : i->fd == 1 ? c.out : c.err) = - redirect (redirect_type::here_doc_ref, *r); + optional<redirect>& ir (i->fd == 0 ? c.in : + i->fd == 1 ? c.out : + c.err); + + // Must be present since it is referenced by here-doc. + // + assert (ir); + + // Note: preserve the original representation. + // + ir = redirect (redirect_type::here_doc_ref, *r, move (ir->token)); } } @@ -1660,6 +1687,8 @@ namespace build2 build2::parser::lexer_ = l; } + static redirect_aliases no_redirect_aliases; + void parser:: apply_value_attributes (const variable* var, value& lhs, @@ -1671,7 +1700,12 @@ namespace build2 path_ = &name; istringstream is (attributes); - lexer l (is, name, lexer_mode::attributes); + + // Note that the redirect alias information is not used in the + // attributes lexer mode. + // + lexer l (is, name, lexer_mode::attributes, no_redirect_aliases); + set_lexer (&l); token t; diff --git a/libbuild2/script/parser.hxx b/libbuild2/script/parser.hxx index ecd9f5a..d47f88e 100644 --- a/libbuild2/script/parser.hxx +++ b/libbuild2/script/parser.hxx @@ -12,6 +12,7 @@ #include <libbuild2/diagnostics.hxx> #include <libbuild2/script/token.hxx> +#include <libbuild2/script/lexer.hxx> // redirect_aliases #include <libbuild2/script/script.hxx> namespace build2 @@ -86,7 +87,7 @@ namespace build2 using here_docs = vector<here_doc>; pair<command_expr, here_docs> - parse_command_expr (token&, token_type&); + parse_command_expr (token&, token_type&, const redirect_aliases&); command_exit parse_command_exit (token&, token_type&); diff --git a/libbuild2/script/run.cxx b/libbuild2/script/run.cxx index 3474ccb..870a70f 100644 --- a/libbuild2/script/run.cxx +++ b/libbuild2/script/run.cxx @@ -266,7 +266,7 @@ namespace build2 eop = path (op + ".orig"); save (eop, - transform (rd.str, false /* regex */, rd.modifiers, env), + transform (rd.str, false /* regex */, rd.modifiers (), env), ll); env.clean_special (eop); @@ -432,14 +432,14 @@ namespace build2 if (l.regex) // Regex (possibly empty), { r += rl.intro; - r += transform (l.value, true /* regex */, rd.modifiers, env); + r += transform (l.value, true /* regex */, rd.modifiers (), env); r += rl.intro; r += l.flags; } else if (!l.special.empty ()) // Special literal. r += rl.intro; else // Textual literal. - r += transform (l.value, false /* regex */, rd.modifiers, env); + r += transform (l.value, false /* regex */, rd.modifiers (), env); r += l.special; return r; @@ -517,7 +517,7 @@ namespace build2 { string s (transform (l.value, true /* regex */, - rd.modifiers, + rd.modifiers (), env)); c = line_char ( @@ -556,7 +556,8 @@ namespace build2 // rls += line_char (transform (l.value, false /* regex */, - rd.modifiers, env), + rd.modifiers (), + env), pool); } @@ -1091,7 +1092,7 @@ namespace build2 isp = std_path ("stdin"); save (isp, - transform (in.str, false /* regex */, in.modifiers, env), + transform (in.str, false /* regex */, in.modifiers (), env), ll); env.clean_special (isp); diff --git a/libbuild2/script/script.cxx b/libbuild2/script/script.cxx index a93315f..5a2eda3 100644 --- a/libbuild2/script/script.cxx +++ b/libbuild2/script/script.cxx @@ -235,44 +235,56 @@ namespace build2 to_stream_q (o, s.str ()); }; - auto print_redirect = [&o, print_path] (const redirect& r, - const char* prefix) + auto print_redirect = [&o, print_path] (const redirect& r, int fd) { - o << ' ' << prefix; + const redirect& er (r.effective ()); - size_t n (string::traits_type::length (prefix)); - assert (n > 0); + // Print the none redirect (no data allowed) if/when the respective + // syntax is invented. + // + if (er.type == redirect_type::none) + return; + + o << ' '; - char d (prefix[n - 1]); // Redirect direction. + // Print the redirect file descriptor. + // + if (fd == 2) + o << fd; + + // Print the redirect original representation and the modifiers, if + // present. + // + r.token.printer (o, r.token, print_mode::raw); - switch (r.type) + // Print the rest of the redirect (file path, etc). + // + switch (er.type) { - case redirect_type::none: assert (false); break; - case redirect_type::pass: o << '|'; break; - case redirect_type::null: o << '-'; break; - case redirect_type::trace: o << '!'; break; - case redirect_type::merge: o << '&' << r.fd; break; + case redirect_type::none: assert (false); break; + case redirect_type::here_doc_ref: assert (false); break; + + case redirect_type::pass: + case redirect_type::null: + case redirect_type::trace: break; + case redirect_type::merge: o << er.fd; break; + + case redirect_type::file: + { + print_path (er.file.path); + break; + } case redirect_type::here_str_literal: case redirect_type::here_doc_literal: { - bool doc (r.type == redirect_type::here_doc_literal); - - // For here-document add another '>' or '<'. Note that here end - // marker never needs to be quoted. - // - if (doc) - o << d; - - o << r.modifiers; - - if (doc) - o << r.end; + if (er.type == redirect_type::here_doc_literal) + o << er.end; else { - const string& v (r.str); + const string& v (er.str); to_stream_q (o, - r.modifiers.find (':') == string::npos + er.modifiers ().find (':') == string::npos ? string (v, 0, v.size () - 1) // Strip newline. : v); } @@ -283,20 +295,10 @@ namespace build2 case redirect_type::here_str_regex: case redirect_type::here_doc_regex: { - bool doc (r.type == redirect_type::here_doc_regex); - - // For here-document add another '>' or '<'. Note that here end - // marker never needs to be quoted. - // - if (doc) - o << d; - - o << r.modifiers; + const regex_lines& re (er.regex); - const regex_lines& re (r.regex); - - if (doc) - o << re.intro + r.end + re.intro + re.flags; + if (er.type == redirect_type::here_doc_regex) + o << re.intro + er.end + re.intro + re.flags; else { assert (!re.lines.empty ()); // Regex can't be empty. @@ -307,23 +309,6 @@ namespace build2 break; } - - case redirect_type::file: - { - // For stdin or stdout-comparison redirect add '>>' or '<<' (and - // so make it '<<<' or '>>>'). Otherwise add '+' or '=' (and so - // make it '>+' or '>='). - // - if (d == '<' || r.file.mode == redirect_fmode::compare) - o << d << d; - else - o << (r.file.mode == redirect_fmode::append ? '+' : '='); - - print_path (r.file.path); - break; - } - - case redirect_type::here_doc_ref: assert (false); break; } }; @@ -358,7 +343,7 @@ namespace build2 } } - o << (r.modifiers.find (':') == string::npos ? "" : "\n") << r.end; + o << (r.modifiers ().find (':') == string::npos ? "" : "\n") << r.end; }; if ((m & command_to_stream::header) == command_to_stream::header) @@ -377,17 +362,14 @@ namespace build2 // Redirects. // - // Print the none redirect (no data allowed) if/when the respective - // syntax is invened. - // - if (c.in && c.in->effective ().type != redirect_type::none) - print_redirect (c.in->effective (), "<"); + if (c.in) + print_redirect (*c.in, 0); - if (c.out && c.out->effective ().type != redirect_type::none) - print_redirect (c.out->effective (), ">"); + if (c.out) + print_redirect (*c.out, 1); - if (c.err && c.err->effective ().type != redirect_type::none) - print_redirect (c.err->effective (), "2>"); + if (c.err) + print_redirect (*c.err, 2); for (const auto& p: c.cleanups) { @@ -513,7 +495,7 @@ namespace build2 redirect:: redirect (redirect&& r) noexcept : type (r.type), - modifiers (move (r.modifiers)), + token (move (r.token)), end (move (r.end)), end_line (r.end_line), end_column (r.end_column) @@ -593,7 +575,7 @@ namespace build2 redirect:: redirect (const redirect& r) : type (r.type), - modifiers (r.modifiers), + token (r.token), end (r.end), end_line (r.end_line), end_column (r.end_column) diff --git a/libbuild2/script/script.hxx b/libbuild2/script/script.hxx index abb2fd7..22747a8 100644 --- a/libbuild2/script/script.hxx +++ b/libbuild2/script/script.hxx @@ -8,7 +8,7 @@ #include <libbuild2/forward.hxx> #include <libbuild2/utility.hxx> -#include <libbuild2/token.hxx> // replay_tokens +#include <libbuild2/token.hxx> #include <libbuild2/variable.hxx> namespace build2 @@ -54,7 +54,7 @@ namespace build2 // Note that the exact spacing and partial quoting may not be restored due // to the information loss. // - LIBBUILD2_SYMEXPORT void + void dump (ostream&, const string& ind, const lines&); // Parse object model. @@ -167,7 +167,10 @@ namespace build2 reference_wrapper<const redirect> ref; // Note: no chains. }; - string modifiers; // Redirect modifiers. + // Modifiers and the original representation (potentially an alias). + // + build2::token token; + string end; // Here-document end marker (no regex intro/flags). uint64_t end_line; // Here-document end marker location. uint64_t end_column; @@ -179,8 +182,10 @@ namespace build2 // Create redirect of the reference type. // - redirect (redirect_type t, const redirect& r) - : type (redirect_type::here_doc_ref), ref (r) + redirect (redirect_type t, const redirect& r, build2::token tk) + : type (redirect_type::here_doc_ref), + ref (r), + token (move (tk)) { // There is no support (and need) for reference chains. // @@ -217,6 +222,12 @@ namespace build2 { return type == redirect_type::here_doc_ref ? ref.get () : *this; } + + const string& + modifiers () const noexcept + { + return token.value; + } }; // cleanup diff --git a/libbuild2/script/token.cxx b/libbuild2/script/token.cxx index 6c9de87..1c612a5 100644 --- a/libbuild2/script/token.cxx +++ b/libbuild2/script/token.cxx @@ -20,24 +20,31 @@ namespace build2 switch (t.type) { - case token_type::clean: os << q << '&' << v << q; break; - case token_type::pipe: os << q << '|' << q; break; - - case token_type::in_pass: os << q << "<|" << q; break; - case token_type::in_null: os << q << "<-" << q; break; - case token_type::in_str: os << q << '<' << v << q; break; - case token_type::in_doc: os << q << "<<" << v << q; break; - case token_type::in_file: os << q << "<<<" << q; break; - - case token_type::out_pass: os << q << ">|" << q; break; - case token_type::out_null: os << q << ">-" << q; break; - case token_type::out_trace: os << q << ">!" << q; break; - case token_type::out_merge: os << q << ">&" << q; break; - case token_type::out_str: os << q << '>' << v << q; break; - case token_type::out_doc: os << q << ">>" << v << q; break; - case token_type::out_file_cmp: os << q << ">>>" << v << q; break; - case token_type::out_file_ovr: os << q << ">=" << v << q; break; - case token_type::out_file_app: os << q << ">+" << v << q; break; + case token_type::clean: os << q << '&' << v << q; break; + case token_type::pipe: os << q << '|' << q; break; + + case token_type::in_pass: os << q << "<|" << q; break; + case token_type::in_null: os << q << "<-" << q; break; + case token_type::in_file: os << q << "<=" << q; break; + case token_type::in_doc: os << q << "<<=" << v << q; break; + case token_type::in_str: os << q << "<<<=" << v << q; break; + + case token_type::out_pass: os << q << ">|" << q; break; + case token_type::out_null: os << q << ">-" << q; break; + case token_type::out_trace: os << q << ">!" << q; break; + case token_type::out_merge: os << q << ">&" << q; break; + case token_type::out_file_ovr: os << q << ">=" << q; break; + case token_type::out_file_app: os << q << ">+" << q; break; + case token_type::out_file_cmp: os << q << ">?" << q; break; + case token_type::out_doc: os << q << ">>?" << v << q; break; + case token_type::out_str: os << q << ">>>?" << v << q; break; + + case token_type::in_l: os << q << '<' << v << q; break; + case token_type::in_ll: os << q << "<<" << v << q; break; + case token_type::in_lll: os << q << "<<<" << v << q; break; + case token_type::out_g: os << q << '>' << v << q; break; + case token_type::out_gg: os << q << ">>" << v << q; break; + case token_type::out_ggg: os << q << ">>>" << v << q; break; default: build2::token_printer (os, t, m); } diff --git a/libbuild2/script/token.hxx b/libbuild2/script/token.hxx index a2ccaee..0186bd9 100644 --- a/libbuild2/script/token.hxx +++ b/libbuild2/script/token.hxx @@ -22,23 +22,33 @@ namespace build2 // NOTE: remember to update token_printer()! pipe = base_type::value_next, // | - clean, // &{?!} (modifiers in value) + clean, // &{?!} (modifiers in value) in_pass, // <| in_null, // <- - in_str, // <{:/} (modifiers in value) - in_doc, // <<{:/} (modifiers in value) - in_file, // <<< + in_file, // <= + in_doc, // <<={:/} (modifiers in value) + in_str, // <<<={:/} (modifiers in value) out_pass, // >| out_null, // >- out_trace, // >! out_merge, // >& - out_str, // >{:/~} (modifiers in value) - out_doc, // >>{:/~} (modifiers in value) - out_file_cmp, // >>> out_file_ovr, // >= out_file_app, // >+ + out_file_cmp, // >? + out_doc, // >>?{:/~} (modifiers in value) + out_str, // >>>?{:/~} (modifiers in value) + + // The modifiers are in the token value, if the redirect the alias + // resolves to supports the modifiers. + // + in_l, // < + in_ll, // << + in_lll, // <<< + out_g, // > + out_gg, // >> + out_ggg, // >>> value_next }; |