aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/parser8
-rw-r--r--build/parser.cxx146
-rw-r--r--tests/names/buildfile54
-rw-r--r--tests/names/test.out37
-rwxr-xr-xtests/names/test.sh3
5 files changed, 217 insertions, 31 deletions
diff --git a/build/parser b/build/parser
index 7787568..04066ff 100644
--- a/build/parser
+++ b/build/parser
@@ -100,6 +100,14 @@ namespace build
const dir_path* dir,
const std::string* type);
+ size_t
+ names_trailer (token&, token_type&,
+ names_type&,
+ size_t pair,
+ const std::string* prj,
+ const dir_path* dir,
+ const std::string* type);
+
// Buildspec.
//
buildspec
diff --git a/build/parser.cxx b/build/parser.cxx
index 8973036..4ce9fba 100644
--- a/build/parser.cxx
+++ b/build/parser.cxx
@@ -839,13 +839,123 @@ namespace build
return ns;
}
+ // Parse names inside {} and handle the following "crosses" (i.e.,
+ // {a b}{x y}) if any. Return the number of names added to the list.
+ //
+ size_t parser::
+ names_trailer (token& t, type& tt,
+ names_type& ns,
+ size_t pair,
+ const string* pp,
+ const dir_path* dp,
+ const string* tp)
+ {
+ next (t, tt); // Get what's after '{'.
+
+ size_t count (ns.size ());
+ names (t, tt,
+ ns,
+ false,
+ (pair != 0
+ ? pair
+ : (ns.empty () || ns.back ().pair == '\0' ? 0 : ns.size ())),
+ pp, dp, tp);
+ count = ns.size () - count;
+
+ if (tt != type::rcbrace)
+ fail (t) << "expected } instead of " << t;
+
+ // See if we have a cross. See tests/names.
+ //
+ if (peek () == type::lcbrace && !peeked ().separated)
+ {
+ next (t, tt); // Get '{'.
+ const location loc (get_location (t, &path_));
+
+ names_type x; // Parse into a separate list of names.
+ names_trailer (t, tt, x, 0, nullptr, nullptr, nullptr);
+
+ if (size_t n = x.size ())
+ {
+ // Now cross the last 'count' names in 'ns' with 'x'. First we will
+ // allocate n - 1 additional sets of last 'count' names in 'ns'.
+ //
+ size_t b (ns.size () - count); // Start of 'count' names.
+ 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[b + j]);
+
+ // Now cross each name, this time including the first set.
+ //
+ for (size_t i (0); i != n; ++i)
+ {
+ for (size_t j (0); j != count; ++j)
+ {
+ name& l (ns[b + i * count + j]);
+ const name& r (x[i]);
+
+ // Move the project names.
+ //
+ if (r.proj != nullptr)
+ {
+ if (l.proj != nullptr)
+ fail (loc) << "nested project name " << *r.proj;
+
+ l.proj = r.proj;
+ }
+
+ // Merge directories.
+ //
+ if (!r.dir.empty ())
+ {
+ if (l.dir.empty ())
+ l.dir = move (r.dir);
+ else
+ l.dir /= r.dir;
+ }
+
+ // 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;
+
+ l.type.swap (l.value);
+ }
+
+ if (!r.type.empty ())
+ {
+ if (!l.type.empty ())
+ fail (loc) << "nested type name " << r.type;
+
+ l.type = move (r.type);
+ }
+
+ l.value = move (r.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 pairs mode to test it, yet.
+ }
+ }
+
+ count *= n;
+ }
+ }
+
+ return count;
+ }
+
void parser::
- names (token& t,
- type& tt,
+ names (token& t, type& tt,
names_type& ns,
bool chunk,
size_t pair,
- const std::string* pp,
+ const string* pp,
const dir_path* dp,
const string* tp)
{
@@ -995,20 +1105,7 @@ namespace build
tp1 = &t1;
}
- next (t, tt);
- count = ns.size ();
- names (t, tt,
- ns,
- false,
- (pair != 0
- ? pair
- : (ns.empty () || ns.back ().pair == '\0' ? 0 : ns.size ())),
- pp1, dp1, tp1);
- count = ns.size () - count;
-
- if (tt != type::rcbrace)
- fail (t) << "expected } instead of " << t;
-
+ count = names_trailer (t, tt, ns, pair, pp1, dp1, tp1);
tt = peek ();
continue;
}
@@ -1295,20 +1392,7 @@ namespace build
//
if (tt == type::lcbrace)
{
- next (t, tt);
- count = ns.size ();
- names (t, tt,
- ns,
- false,
- (pair != 0
- ? pair
- : (ns.empty () || ns.back ().pair == '\0' ? 0 : ns.size ())),
- pp, dp, tp);
- count = ns.size () - count;
-
- if (tt != type::rcbrace)
- fail (t) << "expected } instead of " << t;
-
+ count = names_trailer (t, tt, ns, pair, pp, dp, tp);
tt = peek ();
continue;
}
diff --git a/tests/names/buildfile b/tests/names/buildfile
new file mode 100644
index 0000000..0f3d482
--- /dev/null
+++ b/tests/names/buildfile
@@ -0,0 +1,54 @@
+# Name separation.
+#
+print foo {bar baz}
+print fox/ {bar baz}
+print fox/foo {bar baz}
+
+# Name "crosses".
+#
+print {}{bar} # Same as bar.
+print {foo}{} # Same as foo{} (empty name of type foo).
+print foo{} # For compatiron.
+print {foo}{bar}
+print {foo}{bar baz}
+print {foo fox}{bar}
+print {foo fox}{bar baz}
+
+print dir/{}{bar} # Same as dir/bar.
+print dir/{foo}{} # Same as dir/foo{} (directory of type foo).
+print dir/foo{} # For comparison.
+print dir/{foo}{bar}
+print dir/{foo}{bar baz}
+print dir/{foo fox}{bar}
+print dir/{foo fox}{bar baz}
+
+print {dir/}{bar}
+print {dir/}{bar baz}
+print {dir/ dor/}{bar}
+print {dir/ dor/}{bar baz}
+
+print {dir/foo}{bar}
+print {dir/foo}{bar baz}
+print {dir/foo dor/fox}{bar}
+print {dir/foo dor/fox}{bar baz}
+
+print {dir/}{foo}{bar}
+print {dir/}{foo}{bar baz}
+print {dir/ dor/}{foo}{bar}
+print {dir/ dor/}{foo fox}{bar baz}
+
+print {prj%foo}{bar baz}
+print {foo}{bar prj%baz}
+#print {prj%foo}{bar prk%baz} # nested project name
+
+print dir/{foo}{bar baz}
+print {foo}{bar dir/{baz}}
+print dir/{foo}{bar dor/{baz}}
+
+print {dir/foo{}}{bar}
+print {dir/{foo}}{bar}
+print {dir/}{foo{bar}}
+#print {dir/foo{fox}}{bar} # nested type name
+#print {dir/foo}{fox{bar}} # nested type name
+
+./:
diff --git a/tests/names/test.out b/tests/names/test.out
new file mode 100644
index 0000000..280c372
--- /dev/null
+++ b/tests/names/test.out
@@ -0,0 +1,37 @@
+foo bar baz
+fox/ bar baz
+fox/foo bar baz
+bar
+foo{}
+foo{}
+foo{bar}
+foo{bar} foo{baz}
+foo{bar} fox{bar}
+foo{bar} fox{bar} foo{baz} fox{baz}
+dir/bar
+foo{dir/}
+foo{dir/}
+dir/foo{bar}
+dir/foo{bar} dir/foo{baz}
+dir/foo{bar} dir/fox{bar}
+dir/foo{bar} dir/fox{bar} dir/foo{baz} dir/fox{baz}
+dir/bar
+dir/bar dir/baz
+dir/bar dor/bar
+dir/bar dor/bar dir/baz dor/baz
+dir/foo{bar}
+dir/foo{bar} dir/foo{baz}
+dir/foo{bar} dor/fox{bar}
+dir/foo{bar} dor/fox{bar} dir/foo{baz} dor/fox{baz}
+dir/foo{bar}
+dir/foo{bar} dir/foo{baz}
+dir/foo{bar} dor/foo{bar}
+dir/foo{bar} dor/foo{bar} dir/fox{bar} dor/fox{bar} dir/foo{baz} dor/foo{baz} dir/fox{baz} dor/fox{baz}
+prj%foo{bar} prj%foo{baz}
+foo{bar} prj%foo{baz}
+dir/foo{bar} dir/foo{baz}
+foo{bar} dir/foo{baz}
+dir/foo{bar} dir/dor/foo{baz}
+dir/foo{bar}
+dir/foo{bar}
+dir/foo{bar}
diff --git a/tests/names/test.sh b/tests/names/test.sh
new file mode 100755
index 0000000..b898b3c
--- /dev/null
+++ b/tests/names/test.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+valgrind -q b -q | diff -u test.out -