libshit

Just some random shit
git clone https://git.neptards.moe/neptards/libshit.git
Log | Files | Refs | Submodules | README | LICENSE

format_opcode_argument.cpp (5193B)


      1 #include "libshit/translate/format_opcode_argument.hpp"
      2 
      3 #include "libshit/doctest.hpp"
      4 #include "libshit/except.hpp"
      5 #include "libshit/maybe_owning_vector.hpp"
      6 #include "libshit/number_format.hpp"
      7 #include "libshit/translate/context.hpp"
      8 #include "libshit/translate/format_test_helper.hpp"
      9 #include "libshit/translate/plural.hpp"
     10 #include "libshit/utils.hpp"
     11 
     12 #include <boost/container/small_vector.hpp> // IWYU pragma: keep
     13 
     14 #include <cstdint>
     15 #include <sstream>
     16 #include <variant>
     17 #include <vector>
     18 
     19 namespace Libshit::Translate
     20 {
     21   TEST_SUITE_BEGIN("Libshit::Translate::Format");
     22   using namespace std::string_literals;
     23   namespace { using ArgOC = FormatOpcodes<FormatOpcode::ARGUMENT>; }
     24 
     25   void ArgOC::UpdateTypes(Types& types)
     26   {
     27     auto code = data.code.GetSpan();
     28     auto index = (code[pc] >> 8) & 0xff;
     29     auto type = (code[pc] >> 16) & 0xff;
     30     SetType(types, index, static_cast<Type>(type));
     31     pc += 2;
     32   }
     33 
     34   void ArgOC::Format(
     35     std::string& out, const Context& ctx, Span<const ArgumentType> args)
     36   {
     37     pc += 2;
     38     if (pc > data.code.size())
     39       LIBSHIT_THROW(InvalidFormat, "Invalid code size",
     40                     "PC", pc, "Size", data.code.size());
     41 
     42     auto code = data.code.GetSpan();
     43     auto index = (code[pc-2] >> 8) & 0xff;
     44     if (index >= args.size())
     45     {
     46       out += "???";
     47       return;
     48     }
     49 
     50     FormatParams fp{code[pc-1]};
     51     Overloaded ol{
     52       [&](auto x)
     53       {
     54         static_assert(std::is_integral_v<decltype(x)>);
     55         auto info = ctx.GetInfo();
     56         FormatInt(out, x, fp.GetDigits(), fp.GetShowPos(), info.digit_sep,
     57                   fp.GetGrouping() ? info.grouping : 0);
     58       },
     59       [&](double d)
     60       {
     61         auto info = ctx.GetInfo();
     62         FormatDouble(
     63           out, d, fp.GetDoubleFormat(), fp.GetDigits(), fp.GetPrecision(),
     64           fp.GetShowPos(), info.digit_sep, info.decimal_sep,
     65           fp.GetGrouping() ? info.grouping : 0);
     66       },
     67       [&](StringView v) { out += v; },
     68       [&](const TranslationPtr& ptr) { out += ptr->Get({fp.GetCaseId()}).str; },
     69       [&](const NotNullSmartPtr<Printer>& p) { p->Print(out); },
     70     };
     71     std::visit(Move(ol), args[index].ToBase());
     72   }
     73 
     74   unsigned ArgOC::RegisterCommon(RegisterArgs&& r, Type type)
     75   {
     76     r.parser.FinishVerb();
     77     r.parser.code.push_back(
     78       FormatOpcode::ARGUMENT | (r.index << 8) | (unsigned(type) << 16));
     79     r.parser.code.push_back(r.fp.GetRaw());
     80     return r.index + 1;
     81   }
     82 
     83   namespace
     84   {
     85     struct X {};
     86     LIBSHIT_TEST_FUN std::ostream& operator<<(std::ostream& os, X)
     87     { return os << "X::op<<"; }
     88   }
     89 
     90   TEST_CASE("Argument format")
     91   {
     92     // ctxp, index, fp
     93 #define REG(ctxp, ...)                                         \
     94     FormatData data;                                           \
     95     Context ctx = ctxp;                                        \
     96     FormatParser parser{{}, ctx, data};                        \
     97     ArgOC::Register<Type::GENERIC>({parser, __VA_ARGS__, {}}); \
     98     parser.Finish()
     99 
    100     auto fmt = Test::Format<FormatOpcode::ARGUMENT>;
    101 #define CHK(exp, argv, ctxp, ...)                   \
    102     do                                              \
    103     {                                               \
    104       REG(ctxp, 0, __VA_ARGS__);                    \
    105       FAST_CHECK_EQ(fmt(parser, {argv}), exp##_ns); \
    106     }                                               \
    107     while (0)
    108 
    109     CHK("3", 3, {}, {});
    110     CHK("3140000", 3.14, {}, {}); // decimal dot is an empty string!
    111     CHK("foo", "foo", {}, {});
    112     CHK("foo", "foo"s, {}, {});
    113     CHK("foo", "foo"_ns, {}, {});
    114     CHK("X::op<<", X{}, {}, {});
    115 
    116     using DF = DoubleFormat;
    117     LanguageInfo a{"", "", "", ".", "'", 3, {}, {}, {}, 0, {}};
    118     CHK("003", 3, {}, {{}, {}, {}, 0, 3, 0});
    119     CHK("3.140", 3.14, a, {{}, {}, {}, 0, 0, 3});
    120     CHK("012.346", 12.34567, a, {{}, {}, {}, 0, 6, 3});
    121     CHK("+01'234", 1234, a, {true, true, {}, 0, 5, 6});
    122     CHK("+0'000'012.340", 12.34, a, {true, true, DF::FIXED, 0, 10, 3});
    123     CHK("+0'001.234e+01", 12.34, a, {true, true, DF::SCIENTIFIC, 0, 7, 3});
    124     CHK("foo", "foo", a, {true, true, DF::SCIENTIFIC, 0, 7, 6});
    125 
    126     LanguageInfo b{"", "", "", "::", "()", 2, {}, {}, {}, 0, {}};
    127     CHK("0()12()34", 1234, b, {false, true, {}, 0, 5, 6});
    128     CHK("1()23::456", 123.456, b, {false, true, DF::GENERIC, 0, 0, 6});
    129 
    130     ArgumentType case_ptr = MakeSmart<Test::Case>();
    131     CHK("aa", case_ptr, a, {{}, {}, {}, 0, 0, 6});
    132     CHK("bb", case_ptr, a, {{}, {}, {}, 1, 0, 6});
    133     CHK("cc", case_ptr, a, {{}, {}, {}, 2, 0, 6});
    134 #undef CHK
    135 
    136     // non first arg
    137     ArgumentType args[] = { 0, "1str", 2.03 };
    138     {
    139       REG({}, 1, {});
    140       // Span(args): workaround gcc bug https://godbolt.org/z/Ev3n7eo3G
    141       CHECK(fmt(parser, Span(args)) == "1str"_ns);
    142     }
    143 
    144     {
    145       REG({}, 2, {});
    146       CHECK(fmt(parser, Span(args)) == "2030000"_ns);
    147     }
    148 
    149     // not enough args
    150     {
    151       REG({}, 0, {});
    152       CHECK(fmt(parser, {}) == "???"_ns);
    153     }
    154 
    155     // FormatParams missing
    156     {
    157       REG({}, 0, {});
    158       data.code = MaybeOwningVector<const std::uint32_t>::Copy(
    159         data.code.GetSpan().subspan(0, 1));
    160       CHECK_THROWS(fmt(parser, {1}));
    161     }
    162   }
    163 #undef REG
    164 
    165   TEST_SUITE_END();
    166 }