From 6dbf4954a67efa284ae9abceb3b02c8642b79a49 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 13 Mar 2017 12:49:29 +0200 Subject: Implement dir/type-aware name group crossing --- build2/parser.cxx | 238 ++++++++++++++++++++++++++---------------------------- 1 file changed, 114 insertions(+), 124 deletions(-) (limited to 'build2/parser.cxx') diff --git a/build2/parser.cxx b/build2/parser.cxx index 2f31b7a..7a84bae 100644 --- a/build2/parser.cxx +++ b/build2/parser.cxx @@ -2593,163 +2593,152 @@ namespace build2 size_t pairn, const optional& pp, const dir_path* dp, - const string* tp) + const string* tp, + bool cross) { assert (!pre_parse_); if (pp) pmode = pattern_mode::ignore; - next (t, tt); // Get what's after '{'. + next (t, tt); // Get what's after '{'. + const location loc (get_location (t)); // Start of names. size_t start (ns.size ()); if (pairn == 0 && start != 0 && ns.back ().pair) pairn = start; - // This can be an ordinary name group or a pattern (with inclusions and - // exclusions). We want to detect which one it is since for patterns we - // want just the list of simple names without pair/dir/type added (those - // are added after the pattern expansion in parse_names_pattern()). - // - // Detecting which one it is is tricky. We cannot just peek at the token - // and look for some wildcards since the pattern can be the result of an - // expansion (or, worse, concatenation). Thus pattern_mode::detect: we are - // going to ask parse_names() to detect for us if the first name is a - // pattern. And if it is, to refrain from adding pair/dir/type. On our - // side we will also have to move the pattern names out of the result. - // - const location loc (get_location (t)); - - optional pat_tt ( - parse_names ( - t, tt, - ns, - pmode == pattern_mode::expand ? pattern_mode::detect : pmode, - false, - what, - separators, - pairn, - pp, dp, tp).pattern); - - if (tt != type::rcbrace) - fail (t) << "expected } instead of " << t; - - size_t count; - - // See if this is a pattern. - // - if (pat_tt) - { - // Move the pattern names our of the result. - // - names ps; - if (start == 0) - ps = move (ns); - else - ps.insert (ps.end (), - make_move_iterator (ns.begin () + start), - make_move_iterator (ns.end ())); - ns.resize (start); - - count = expand_name_pattern ( - loc, move (ps), ns, what, pairn, dp, tp, *pat_tt); - } - else - count = ns.size () - start; + names r; - // See if we have a cross. See tests/names. + // Parse names until closing '}' expanding patterns. // - if (peek () == type::lcbrace && !peeked ().separated) + auto parse = [&r, &t, &tt, pmode, what, separators, this] ( + const optional& pp, + const dir_path* dp, + const string* tp) { - next (t, tt); // Get '{'. const location loc (get_location (t)); - names x; // Parse into a separate list of names. - parse_names_trailer ( - t, tt, x, pmode, what, separators, 0, nullopt, nullptr, nullptr); + size_t start (r.size ()); + + // This can be an ordinary name group or a pattern (with inclusions and + // exclusions). We want to detect which one it is since for patterns we + // want just the list of simple names without pair/dir/type added (those + // are added after the pattern expansion in parse_names_pattern()). + // + // Detecting which one it is is tricky. We cannot just peek at the token + // and look for some wildcards since the pattern can be the result of an + // expansion (or, worse, concatenation). Thus pattern_mode::detect: we + // are going to ask parse_names() to detect for us if the first name is + // a pattern. And if it is, to refrain from adding pair/dir/type. + // + optional pat_tt ( + parse_names ( + t, tt, + r, + pmode == pattern_mode::expand ? pattern_mode::detect : pmode, + false, + what, + separators, + 0, // Handled by the splice_names() call below. + pp, dp, tp, false).pattern); - size_t n (x.size ()); + if (tt != type::rcbrace) + fail (t) << "expected '}' instead of " << t; - // If LHS or RHS are empty, then the result is empty as well. + // See if this is a pattern. // - if (count != 0 && n != 0) + if (pat_tt) { - // Now cross the last 'count' names in 'ns' with 'x'. First we will - // allocate n - 1 additional sets of last 'count' names in 'ns'. - // - ns.reserve (ns.size () + count * (n - 1)); - for (size_t i (0); i != n - 1; ++i) - for (size_t j (0); j != count; ++j) - ns.push_back (ns[start + j]); - - // Now cross each name, this time including the first set. + // Move the pattern names our of the result. // - for (size_t i (0); i != n; ++i) - { - for (size_t j (0); j != count; ++j) - { - name& l (ns[start + i * count + j]); - const name& r (x[i]); + names ps; + if (start == 0) + ps = move (r); + else + ps.insert (ps.end (), + make_move_iterator (r.begin () + start), + make_move_iterator (r.end ())); + r.resize (start); - // Move the project names. - // - if (r.proj) - { - if (l.proj) - fail (loc) << "nested project name " << *r.proj; + expand_name_pattern (loc, move (ps), r, what, 0, dp, tp, *pat_tt); + } + }; - l.proj = move (r.proj); - } + // Parse and expand the first group. + // + parse (pp, dp, tp); - // Merge directories. - // - if (!r.dir.empty ()) - { - if (l.dir.empty ()) - l.dir = move (r.dir); - else - l.dir /= r.dir; - } + // Handle crosses. The overall plan is to take what's in r, cross each + // element with the next group using the re-parse machinery, and store the + // result back to r. + // + while (cross && peek () == type::lcbrace && !peeked ().separated) + { + next (t, tt); // Get '{'. - // Figure out the type. As a first step, "promote" the lhs value - // to type. - // - if (!l.value.empty ()) - { - if (!l.type.empty ()) - fail (loc) << "nested type name " << l.value; + names ln (move (r)); + r.clear (); - l.type.swap (l.value); - } + // Cross with empty LHS/RHS is empty. Handle the LHS case now by parsing + // and discaring RHS (empty RHS is handled "naturally" below). + // + if (ln.size () == 0) + { + parse (nullopt, nullptr, nullptr); + r.clear (); + continue; + } - if (!r.type.empty ()) - { - if (!l.type.empty ()) - fail (loc) << "nested type name " << r.type; + replay_guard rg (*this, ln.size () > 1); + for (auto i (ln.begin ()), e (ln.end ()); i != e; ) + { + next (t, tt); // Get what's after '{'. + const location loc (get_location (t)); - l.type = move (r.type); - } + name& l (*i); - l.value = move (r.value); + // "Promote" the lhs value to type. + // + if (!l.value.empty ()) + { + if (!l.type.empty ()) + fail (loc) << "nested type name " << l.value; - // @@ TODO: need to handle pairs on lhs. I think all that needs - // to be done is skip pair's first elements. Maybe also check - // that there are no pairs on the rhs. There is just no easy - // way to enable the value mode to test it, yet. - } + l.type.swap (l.value); } - count *= n; - } - else if (count != 0) - { - ns.resize (start); // Drop LHS. - count = 0; + parse (l.proj, + l.dir.empty () ? nullptr : &l.dir, + l.type.empty () ? nullptr : &l.type); + + if (++i != e) + rg.play (); // Replay. } } - return count; + // Splice the names into the result. Note that we have already handled + // project/dir/type qualification but may still have a pair. Fast-path + // common cases. + // + if (pairn == 0) + { + if (start == 0) + ns = move (r); + else + ns.insert (ns.end (), + make_move_iterator (r.begin ()), + make_move_iterator (r.end ())); + } + else + splice_names (loc, + names_view (r), move (r), + ns, what, + pairn, + nullopt, nullptr, nullptr); + + return ns.size () - start; } // Slashe(s) plus '%'. Note that here we assume '/' is there since that's @@ -2768,7 +2757,8 @@ namespace build2 size_t pairn, const optional& pp, const dir_path* dp, - const string* tp) -> parse_names_result + const string* tp, + bool cross) -> parse_names_result { // Note that support for pre-parsing is partial, it does not handle // groups ({}). @@ -3131,7 +3121,7 @@ namespace build2 } count = parse_names_trailer ( - t, tt, ns, pmode, what, separators, pairn, *pp1, dp1, tp1); + t, tt, ns, pmode, what, separators, pairn, *pp1, dp1, tp1, cross); tt = peek (); continue; } @@ -3537,7 +3527,7 @@ namespace build2 if (tt == type::lcbrace) { count = parse_names_trailer ( - t, tt, ns, pmode, what, separators, pairn, pp, dp, tp); + t, tt, ns, pmode, what, separators, pairn, pp, dp, tp, cross); tt = peek (); continue; } -- cgit v1.1