aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build2/buildfile15
-rw-r--r--build2/parser6
-rw-r--r--build2/parser.cxx238
-rw-r--r--old-tests/names/buildfile8
4 files changed, 130 insertions, 137 deletions
diff --git a/build2/buildfile b/build2/buildfile
index 2c3e191..856346b 100644
--- a/build2/buildfile
+++ b/build2/buildfile
@@ -4,7 +4,6 @@
import libs = libbutl%lib{butl}
-#\
exe{b}: \
{hxx ixx cxx}{ algorithm } \
{ cxx}{ b } \
@@ -95,18 +94,12 @@ test/script/{hxx cxx}{ runner } \
test/script/{hxx ixx cxx}{ script } \
test/script/{hxx cxx}{ token } \
liba{b} $libs
-#\
-
-#exe{b}: {hxx ixx txx cxx}{** -b-options -dummy} \
-# {hxx ixx cxx}{ b-options } \
-# liba{b} $libs
-exe{b}: hxx{** -b-options -dummy} \
- ixx{** -b-options -dummy} \
- txx{** -b-options -dummy} \
- cxx{** -b-options -dummy} \
- {hxx ixx cxx}{b-options} \
+#\
+exe{b}: {hxx ixx txx cxx}{** -b-options -dummy} \
+ {hxx ixx cxx}{b-options} \
liba{b} $libs
+#\
# Fake utility library (without it code generation does not work).
#
diff --git a/build2/parser b/build2/parser
index 91d574b..0855e89 100644
--- a/build2/parser
+++ b/build2/parser
@@ -285,7 +285,8 @@ namespace build2
size_t pairn = 0,
const optional<string>& prj = nullopt,
const dir_path* dir = nullptr,
- const string* type = nullptr);
+ const string* type = nullptr,
+ bool cross = true);
size_t
parse_names_trailer (token&, token_type&,
@@ -296,7 +297,8 @@ namespace build2
size_t pairn,
const optional<string>& prj,
const dir_path* dir,
- const string* type);
+ const string* type,
+ bool cross);
size_t
expand_name_pattern (const location&,
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<string>& 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<const target_type*> 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<string>& 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<const target_type*> 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<string>& 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;
}
diff --git a/old-tests/names/buildfile b/old-tests/names/buildfile
index 0f3d482..adeee15 100644
--- a/old-tests/names/buildfile
+++ b/old-tests/names/buildfile
@@ -51,4 +51,12 @@ print {dir/}{foo{bar}}
#print {dir/foo{fox}}{bar} # nested type name
#print {dir/foo}{fox{bar}} # nested type name
+# Nested replay.
+#
+#file{foo} file{bar}: x = {foo fox}{bar baz}
+
+# Pair.
+#
+print x@{dir/ dor/}{foo fox}{bar baz}
+
./: