Search code examples
c++boost-spirit

Boost spirit fixed point as integer parser


I have the following parser,

    #define PRICE_MULT 10000
    qi::uint_parser<uint32_t, 10, 1, 6> int_part;
    qi::uint_parser<uint32_t, 10, 1, 6> dec_part;

    qi::rule<Iterator, uint64_t()> fixed_point =
         (int_part >> "." >> dec_part )[qi::_val = qi::_1 * PRICE_MULT + qi::_2];


    m_wire_msg = ( qi::as_string[*qi::alpha] >> "," // symbol
                >> qi::ulong_long >> ","        // symbol seq num
                >> qi::ulong_long >> ","        // order id
                >> (fixed_point | qi::ulong_) >> ","
                >> qi::uint_ >> ","                   // volume
                >> qi::char_ >> ","                   // side
                >> +qi::space >> ","                  // firm id
                >> qi::ushort_                        // parity split

But it fails to parse,

"AAPL,1192544,16044086616349464,157.47,100,S,     ,0"

Specifically, the 157.47 can sometimes be whole numbers, for eg. 157 on its own, which is why i'm using "(fixed_point | qi::ulong_)".

But parsing fails, which I don't quite understand. Ideally is should also parse the following correctly,

"AAPL,1192544,16044086616349464,157,100,S,     ,0"

Any help is much appreciated!


Solution

  • Use optional parser to not fail if there is no fractional part.

    Also you had PRICE_MULT with 4 zeros, but dec_part was allowed up to 6 digits.

    #define PRICE_MULT 10000
    qi::uint_parser<std::uint32_t, 10, 1, 6> int_part;
    qi::uint_parser<std::uint32_t, 10, 1, 4> dec_part;
    
    qi::rule<Iterator, std::uint64_t()> fixed_point =
          int_part[qi::_val = qi::_1 * PRICE_MULT]
        >> -('.' >> dec_part[qi::_val += qi::_1]);
    

    https://wandbox.org/permlink/H46ujDgJ57gyE69I

    The right fixed point parser can look like this:

    constexpr std::uint64_t lut[] = { 1, 10, 100, 1000, 10000, 100000, 1000000 };
    constexpr int frac_digits = 6;
    qi::uint_parser<std::uint32_t, 10, 1, 6> const int_part;
    qi::uint_parser<std::uint32_t, 10, 1, frac_digits> const dec_part;
    qi::rule<Iterator, std::uint64_t()> fixed_point
        = int_part[qi::_val = qi::_1 * lut[frac_digits]]           // parse integer part and save multiplied by lut[frac_digits]
       >> ( !qi::lit('.')                                          // do not fail on missing fractional part
          | ('.' >>  qir::iter_pos >> dec_part >> qir::iter_pos)[  // parse fraction and save/syntesize iterators before and after
                        qi::_val += qi::_2 * phx::ref(lut)[        // multiple fraction by lookuped pow10 value
                                                frac_digits - (qi::_3 - qi::_1)]]
            >> *qi::digit);                                        // consume unparsed digits
    

    https://wandbox.org/permlink/mtdMDPzB2RjPxQlD

    input          result
    =====          ======
    123            123000000 
    123.0          123000000
    123.4          123400000
    123.45         123450000
    123.456        123456000
    123.4567       123456700
    123.45678      123456780
    123.456789     123456789
    123.456789123  123456789
    123.           fail
    .123           fail