diff options
-rw-r--r-- | libbuild2/functions-builtin.cxx | 55 | ||||
-rw-r--r-- | libbuild2/utility.hxx | 1 | ||||
-rw-r--r-- | libbuild2/variable.cxx | 42 | ||||
-rw-r--r-- | tests/function/builtin/testscript | 8 |
4 files changed, 93 insertions, 13 deletions
diff --git a/libbuild2/functions-builtin.cxx b/libbuild2/functions-builtin.cxx index 93f2d0f..5129a05 100644 --- a/libbuild2/functions-builtin.cxx +++ b/libbuild2/functions-builtin.cxx @@ -32,6 +32,56 @@ namespace build2 return r; }; + static const char hex_digits[] = "0123456789abcdef"; + + static string + to_string (uint64_t i, optional<value> base, optional<value> width) + { + uint64_t b (base ? convert<uint64_t> (move (*base)) : 10); + size_t w (width + ? static_cast<size_t> (convert<uint64_t> (move (*width))) + : 0); + + // One day we can switch to C++17 std::to_chars(). + // + string r; + switch (b) + { + case 10: + { + r = to_string (i); + if (w > r.size ()) + r.insert (0, w - r.size (), '0'); + break; + } + case 16: + { + r.reserve (18); + r += "0x"; + + for (size_t j (64); j != 0; ) + { + j -= 4; + size_t d ((i >> j) & 0x0f); + + // Omit leading zeros but watch out for the i==0 corner case. + // + if (d != 0 || r.size () != 2 || j == 0) + r += hex_digits[d]; + } + + if (w > r.size () - 2) + r.insert (2, w - (r.size () - 2), '0'); + + break; + } + default: + throw invalid_argument ("unsupported base"); + } + + return r; + } + void builtin_functions (function_map& m) { @@ -77,7 +127,10 @@ namespace build2 // f["string"] += [](bool b) {return b ? "true" : "false";}; f["string"] += [](int64_t i) {return to_string (i);}; - f["string"] += [](uint64_t i) {return to_string (i);}; + f["string"] += [](uint64_t i, optional<value> base, optional<value> width) + { + return to_string (i, move (base), move (width)); + }; // Quote a value returning its string representation. If escape is true, // then also escape (with a backslash) the quote characters being added diff --git a/libbuild2/utility.hxx b/libbuild2/utility.hxx index c82dcc2..a285b03 100644 --- a/libbuild2/utility.hxx +++ b/libbuild2/utility.hxx @@ -69,6 +69,7 @@ namespace build2 using butl::alpha; using butl::alnum; using butl::digit; + using butl::wspace; using butl::trim; using butl::next_word; diff --git a/libbuild2/variable.cxx b/libbuild2/variable.cxx index 4bd01dc..9cdad0b 100644 --- a/libbuild2/variable.cxx +++ b/libbuild2/variable.cxx @@ -515,13 +515,22 @@ namespace build2 { try { - // May throw invalid_argument or out_of_range. - // - size_t i; - int64_t r (stoll (n.value, &i)); + const string& v (n.value); + + if (!wspace (v[0])) + { + // Note that unlike uint64, we don't support hex notation for int64. + + // May throw invalid_argument or out_of_range. + // + size_t i; + int64_t r (stoll (v, &i)); + + if (i == v.size ()) + return r; - if (i == n.value.size ()) - return r; + // Fall through. + } // Fall through. } @@ -563,13 +572,22 @@ namespace build2 { try { - // May throw invalid_argument or out_of_range. - // - size_t i; - uint64_t r (stoull (n.value, &i)); + const string& v (n.value); + + if (!wspace (v[0])) + { + int b (v[0] == '0' && (v[1] == 'x' || v[1] == 'X') ? 16 : 10); + + // May throw invalid_argument or out_of_range. + // + size_t i; + uint64_t r (stoull (v, &i, b)); + + if (i == v.size ()) + return r; - if (i == n.value.size ()) - return r; + // Fall through. + } // Fall through. } diff --git a/tests/function/builtin/testscript b/tests/function/builtin/testscript index 00d594b..02c73ee 100644 --- a/tests/function/builtin/testscript +++ b/tests/function/builtin/testscript @@ -77,6 +77,14 @@ $* <'print $type($identity(abc))' >'' : untyped } +: string +: +{ + $* <'print $string([uint64] 0xffff)' >'65535' : uint + $* <'print $string([uint64] 0xffff, 16)' >'0xffff' : uint-hex + $* <'print $string([uint64] 0xffff, 16, 8)' >'0x0000ffff' : uint-hex-width +} + : sort : { |