From d787f5161414af0910e6992a248ed270d413e2e2 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sat, 28 Nov 2015 19:13:29 +0200 Subject: Add support for name crosses, for example {hxx ixx cxx}{foo bar} See tests/names for more examples. --- build/parser | 8 +++ build/parser.cxx | 146 +++++++++++++++++++++++++++++++++++++++----------- tests/names/buildfile | 54 +++++++++++++++++++ tests/names/test.out | 37 +++++++++++++ tests/names/test.sh | 3 ++ 5 files changed, 217 insertions(+), 31 deletions(-) create mode 100644 tests/names/buildfile create mode 100644 tests/names/test.out create mode 100755 tests/names/test.sh 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 - -- cgit v1.1