aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build2/name19
-rw-r--r--build2/parser.cxx2
-rw-r--r--build2/variable15
-rw-r--r--build2/variable.cxx74
-rw-r--r--build2/variable.ixx9
-rw-r--r--build2/variable.txx27
-rw-r--r--tests/variable/representation/buildfile8
-rw-r--r--tests/variable/representation/test.out8
8 files changed, 116 insertions, 46 deletions
diff --git a/build2/name b/build2/name
index 99c21f4..a4de95c 100644
--- a/build2/name
+++ b/build2/name
@@ -68,19 +68,24 @@ namespace build2
bool
empty () const {return dir.empty () && value.empty ();}
- // Note that strictly speaking the following tests should be
- // orthogonal to qualification. However, the vast majority of
- // cases where we expect a simple or directory name, we also
- // expect it to be unqualified.
+ // Note that strictly speaking the following tests should be orthogonal
+ // to qualification. However, the vast majority of cases where we expect
+ // a simple or directory name, we also expect it to be unqualified.
//
// Note also that empty name is simple but not a directory.
//
bool
- simple () const {return unqualified () && untyped () && dir.empty ();}
+ simple (bool ignore_qual = false) const
+ {
+ return (ignore_qual || unqualified ()) && untyped () && dir.empty ();
+ }
bool
- directory () const
- {return unqualified () && untyped () && !dir.empty () && value.empty ();}
+ directory (bool ignore_qual = false) const
+ {
+ return (ignore_qual || unqualified ()) &&
+ untyped () && !dir.empty () && value.empty ();
+ }
const std::string* proj = nullptr; // Points to project_name_pool.
dir_path dir;
diff --git a/build2/parser.cxx b/build2/parser.cxx
index e1d63aa..ab9012c 100644
--- a/build2/parser.cxx
+++ b/build2/parser.cxx
@@ -1064,7 +1064,7 @@ namespace build2
// Should evaluate to true or false.
//
- if (ns.size () != 1 || !value_traits<bool>::assign (ns[0]))
+ if (ns.size () != 1 || !assign<bool> (ns[0]))
fail (nsl) << "expected " << k << "-expression to evaluate to "
<< "'true' or 'false' instead of '" << ns << "'";
diff --git a/build2/variable b/build2/variable
index a2490ac..029aafa 100644
--- a/build2/variable
+++ b/build2/variable
@@ -214,10 +214,15 @@ namespace build2
template <typename T> typename value_traits<T>::const_type as (const value&);
// Try to "assign" a simple value type to the value stored in name. Return
- // false if the value is not valid for this type.
+ // false if the value is not valid for this type. The second version is
+ // called for a pair and it is expected to merge the result into the first
+ // name.
//
template <typename T> bool assign (name&);
+ template <typename T> bool assign (name&, name&);
+ // Name cast. Can only be called after assign() above returned true.
+ //
template <typename T> typename value_traits<T>::type as (name&);
template <typename T> typename value_traits<T>::const_type as (const name&);
@@ -270,7 +275,7 @@ namespace build2
static type as (value&);
static const_type as (const value&);
- static bool assign (name&);
+ static bool assign (name&, name*);
static void assign (value&, bool);
static void append (value&, bool);
@@ -293,7 +298,7 @@ namespace build2
static type as (value&);
static const_type as (const value&);
- static bool assign (name&);
+ static bool assign (name&, name*);
static void assign (value&, string);
static void append (value&, string);
@@ -316,7 +321,7 @@ namespace build2
static type as (value&);
static const_type as (const value&);
- static bool assign (name&);
+ static bool assign (name&, name*);
static void assign (value&, dir_path);
static void append (value&, dir_path);
@@ -339,7 +344,7 @@ namespace build2
static type as (value&);
static const_type as (const value&);
- static bool assign (name&) {return true;}
+ static bool assign (name&, name* r) {return r == nullptr;}
static void assign (value&, name);
static void append (value&, name) = delete;
diff --git a/build2/variable.cxx b/build2/variable.cxx
index 39d8cdd..ce06d3e 100644
--- a/build2/variable.cxx
+++ b/build2/variable.cxx
@@ -123,9 +123,9 @@ namespace build2
// bool value
//
bool value_traits<bool>::
- assign (name& n)
+ assign (name& n, name* r)
{
- if (n.simple ())
+ if (r == nullptr && n.simple ())
{
const string& s (n.value);
@@ -145,7 +145,7 @@ namespace build2
{
name& n (v.front ());
- if (value_traits<bool>::assign (n))
+ if (assign<bool> (n))
return true;
}
@@ -178,47 +178,64 @@ namespace build2
// string value
//
bool value_traits<string>::
- assign (name& n)
+ assign (name& n, name* r)
{
- // The below code is quite convoluted because we don't want to
- // modify the name until we know it good (if it is not, then it
- // will most likely be printed by the caller in diagnostics).
-
- // Suspend project qualification.
+ // The goal is to reverse the name into its original representation. The
+ // code is a bit convoluted because we try to avoid extra allocation for
+ // the common cases (unqualified, unpaired simple name or directory).
//
- const string* p (n.proj);
- n.proj = nullptr;
- // Convert directory to string.
+ // We can only convert project-qualified simple and directory names.
//
- if (n.directory ())
+ if (!(n.simple (true) || n.directory (true)) ||
+ !(r == nullptr || r->simple (true) || r->directory (true)))
+ return false;
+
+ if (n.directory (true))
{
n.value = move (n.dir).string (); // Move string out of path.
- // Add / back to the end of the path unless it is already there.
- // Note that the string cannot be empty (n.directory () would
- // have been false).
+ // Add / back to the end of the path unless it is already there. Note
+ // that the string cannot be empty (n.directory () would have been
+ // false).
//
if (!dir_path::traits::is_separator (n.value[n.value.size () - 1]))
n.value += '/';
}
- if (!n.simple ())
- {
- n.proj = p; // Restore.
- return false;
- }
-
// Convert project qualification to its string representation.
//
- if (p != nullptr)
+ if (n.qualified ())
{
- string s (*p);
+ string s (*n.proj);
s += '%';
s += n.value;
s.swap (n.value);
}
+ // The same for the RHS of a pair, if we have one.
+ //
+ if (r != nullptr)
+ {
+ n.value += '@';
+
+ if (r->qualified ())
+ {
+ n.value += *r->proj;
+ n.value += '%';
+ }
+
+ if (r->directory (true))
+ {
+ n.value += r->dir.string ();
+
+ if (!dir_path::traits::is_separator (n.value[n.value.size () - 1]))
+ n.value += '/';
+ }
+ else
+ n.value += r->value;
+ }
+
return true;
}
@@ -236,7 +253,7 @@ namespace build2
{
name& n (v.front ());
- if (value_traits<string>::assign (n))
+ if (assign<string> (n))
return !n.value.empty ();
}
@@ -271,8 +288,11 @@ namespace build2
// dir_path value
//
bool value_traits<dir_path>::
- assign (name& n)
+ assign (name& n, name* r)
{
+ if (r != nullptr)
+ return false;
+
if (n.directory ())
return true;
@@ -304,7 +324,7 @@ namespace build2
{
name& n (v.front ());
- if (value_traits<dir_path>::assign (n))
+ if (assign<dir_path> (n))
return !n.dir.empty ();
}
diff --git a/build2/variable.ixx b/build2/variable.ixx
index b368c6e..df3f0c5 100644
--- a/build2/variable.ixx
+++ b/build2/variable.ixx
@@ -34,7 +34,14 @@ namespace build2
inline bool
assign (name& n)
{
- return value_traits<T>::assign (n);
+ return value_traits<T>::assign (n, nullptr);
+ }
+
+ template <typename T>
+ inline bool
+ assign (name& l, name& r)
+ {
+ return value_traits<T>::assign (l, &r);
}
template <typename T>
diff --git a/build2/variable.txx b/build2/variable.txx
index 54e48a3..7a0d53a 100644
--- a/build2/variable.txx
+++ b/build2/variable.txx
@@ -12,13 +12,30 @@ namespace build2
bool
vector_assign (names& v, const variable& var)
{
- // Verify each element has valid value of T.
+ // Verify each element has valid value of T. Merge pairs.
//
- for (name& n: v)
+ for (auto i (v.begin ()); i != v.end (); )
{
- if (!assign<T> (n))
- fail << "invalid " << value_traits<T>::value_type.name << " element "
- << "'" << n << "' in variable '" << var.name << "'";
+ name& n (*i);
+
+ if (n.pair)
+ {
+ name& r (*++i);
+
+ if (!assign<T> (n, r))
+ fail << "invalid " << value_traits<T>::value_type.name
+ << " pair '" << n << "'@'" << r << "'"
+ << " in variable '" << var.name << "'";
+
+ i = v.erase (i);
+ }
+ else
+ {
+ if (!assign<T> (n))
+ fail << "invalid " << value_traits<T>::value_type.name
+ << " element '" << n << "' in variable '" << var.name << "'";
+ ++i;
+ }
}
return !v.empty ();
diff --git a/tests/variable/representation/buildfile b/tests/variable/representation/buildfile
index 7a2c3f2..8410c96 100644
--- a/tests/variable/representation/buildfile
+++ b/tests/variable/representation/buildfile
@@ -10,12 +10,20 @@ val += ///
val += //foo/
#val += dir{-L/}
+# Note that this is "reversed" when we assign it to test.options
+# since that variable is of type strings.
+#
+val += foo@bar foo/@bar/ foo@ @bar @ "@@"
+
val += foo%bar
val += foo%
val += %bar
val += foo%{bar}
#val += foo%file{x}
+val += x%foo@y%bar
+val += x%foo/@y%bar/
+
using cxx
cxx.ext = cxx
diff --git a/tests/variable/representation/test.out b/tests/variable/representation/test.out
index 04d8528..84e7250 100644
--- a/tests/variable/representation/test.out
+++ b/tests/variable/representation/test.out
@@ -6,7 +6,15 @@
'//'
'///'
'//foo/'
+'foo@bar'
+'foo/@bar/'
+'foo@'
+'@bar'
+'@'
+'@@'
'foo%bar'
'foo%'
'%bar'
'foo%bar'
+'x%foo@y%bar'
+'x%foo/@y%bar/'