Danny Robson
f6056153e3
This places, at long last, the core library code into the same namespace as the extended library code.
219 lines
5.9 KiB
Ragel
219 lines
5.9 KiB
Ragel
/*
|
|
* 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 <danny@nerdcruft.net>
|
|
*/
|
|
|
|
#include "./format.hpp"
|
|
|
|
#include <iostream>
|
|
|
|
// 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<std::underlying_type_t<type_t>> (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<const char*> fmt)
|
|
{
|
|
std::vector<specifier> 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<const char*> fmt)
|
|
{
|
|
std::vector<specifier> 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)};
|
|
}
|