/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2017 Danny Robson */ #include "./format.hpp" #include // We generate some really old style C code via ragel here, so we have to // disable some noisy warnings (doubly so given -Werror) #pragma GCC diagnostic ignored "-Wold-style-cast" namespace cruft::format { std::ostream& operator<< (std::ostream &os, type_t val) { switch (val) { case type_t::LITERAL: return os << "LITERAL"; case type_t::USER: return os << "USER"; case type_t::ESCAPE: return os << "ESCAPE"; case type_t::SIGNED: return os << "SIGNED"; case type_t::UNSIGNED: return os << "UNSIGNED"; case type_t::REAL: return os << "REAL"; case type_t::STRING: return os << "STRING"; case type_t::CHAR: return os << "CHAR"; case type_t::POINTER: return os << "POINTER"; case type_t::COUNT: return os << "COUNT"; } return os << "UNKNOWN_" << static_cast> (val); } std::ostream& operator<< (std::ostream &os, const specifier &val) { return os << "{ fmt: " << val.fmt << ", type: " << val.type << " }"; } } /////////////////////////////////////////////////////////////////////////////// %%{ machine printf; parameter = digit+ '$'; flag = '+' %{ s.flags.plus = true; } | '-' %{ s.flags.minus = true; } | ' ' %{ s.flags.space = true; } | '0' %{ s.flags.zero = true; } | '#' %{ s.flags.hash = true; } ; width = digit+ >{ s.width = 0; } ${ s.width *= 10; s.width += fc - '0'; }; # precision may be zero digits which implies zero precision. precision = '.' >{ s.precision = 0; } digit* ${ s.precision *= 10; s.precision += fc - '0'; }; length = 'hh' %{ s.length = sizeof (char); } | 'h' %{ s.length = sizeof (short); } | 'l' %{ s.length = sizeof (long); } | 'll' %{ s.length = sizeof (long long); } | 'L' %{ s.length = sizeof (long double); } | 'z' %{ s.length = sizeof (size_t); } | 'j' %{ s.length = sizeof (intmax_t); } | 't' %{ s.length = sizeof (ptrdiff_t); } ; type = ( '!' >{ s.type = type_t::USER; } | '%' >{ s.type = type_t::ESCAPE; } | ( 'd' | 'i' ) >{ s.type = type_t::SIGNED; } | ( 'u' | 'x' %{ s.base = 16; } | 'X' %{ s.base = 16; s.upper = true; } | 'o' %{ s.base = 8; } ) >{ s.type = type_t::UNSIGNED; } | ( ('f' | 'F' %{ s.upper = true; }) %{ s.representation = specifier::FIXED; } | ('e' | 'E' %{ s.upper = true; }) %{ s.representation = specifier::SCIENTIFIC; } | ('g' | 'G' %{ s.upper = true; }) %{ s.representation = specifier::DEFAULT; } | ('a' | 'A' %{ s.upper = true; }) %{ s.representation = specifier::HEX; s.base = 16; } ) >{ s.type = type_t::REAL; } | 's' >{ s.type = type_t::STRING; } | 'c' >{ s.type = type_t::CHAR; } | 'p' >{ s.type = type_t::POINTER; } | 'n' >{ s.type = type_t::COUNT; } ); literal = ([^%]+) >{ s = specifier {}; s.fmt = {fpc,fpc}; s.type = type_t::LITERAL; } %{ s.fmt = {s.fmt.begin(),fpc}; if (!s.fmt.empty ()) { specs.push_back (s); } }; specifier = ( '%' parameter? flag** width? precision? length? type ) >{ s = specifier {}; s.fmt = {fpc,fpc}; } %{ s.fmt = {s.fmt.begin(),fpc}; specs.push_back (s); }; format := literal? (specifier literal?)** >{ success = false; } %{ success = true; } ; write data; }%% /////////////////////////////////////////////////////////////////////////////// cruft::format::parsed cruft::format::printf (cruft::view fmt) { std::vector specs; specifier s; bool success = false; (void)s; int cs; auto p = std::cbegin (fmt); auto pe = std::cend (fmt); auto eof = pe; %%write init; %%write exec; if (!success) throw std::runtime_error ("invalid format specification"); return parsed { std::move (specs) }; } /// parses a format specifier in the style of PEP3101 (with the notable /// exception of named parameters). /// /// in the event of a parsing error the function will throw. makes no /// attempt to cater for constexpr validation. cruft::format::parsed cruft::format::python (cruft::view fmt) { std::vector specs; const auto *prev = std::begin (fmt); const auto *cursor = std::begin (fmt); while (*cursor) { switch (*cursor) { case '{': { { specifier s; s.fmt = { prev, cursor }; s.type = type_t::LITERAL; specs.push_back (s); } auto first = cursor; while (*++cursor != '}') ; ++cursor; { specifier s; s.fmt = {first, cursor}; s.type = type_t::USER; specs.push_back (s); } prev = cursor; break; } default: ++cursor; break; } } { specifier s; s.fmt = {prev,cursor}; s.type = type_t::LITERAL; specs.push_back (s); } return parsed {std::move (specs)}; }