216 lines
5.2 KiB
C++
216 lines
5.2 KiB
C++
#include "printf.h"
|
|
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
|
|
std::string mini_sprintf_format(bool left_align, bool sign, bool space_on_left, bool padding_by_zero, int width, int precision, int length, char specifier, const MlValue &value) {
|
|
std::string s;
|
|
bool is_positive = false;
|
|
|
|
if (specifier == 's') {
|
|
return value.as_string();
|
|
}
|
|
if (specifier == 'i' || specifier == 'd') {
|
|
int ival = value.as_int();
|
|
is_positive = ival >= 0;
|
|
|
|
s = std::to_string(ival);
|
|
} else if (specifier == 'f' || specifier == 'e') {
|
|
double dval = value.as_float();
|
|
is_positive = dval >= 0;
|
|
|
|
std::ostringstream stream_str;
|
|
if (specifier == 'f')
|
|
stream_str << std::fixed;
|
|
else
|
|
stream_str << std::scientific;
|
|
|
|
if (precision >= 0)
|
|
stream_str << std::setprecision(precision);
|
|
|
|
stream_str << dval;
|
|
s = stream_str.str(); // TODO ??
|
|
}
|
|
|
|
if (width > -1 && s.size() < width) {
|
|
int new_width = width - (sign && is_positive ? 1 : 0); // spce for + sign
|
|
if (s.size() < new_width) {
|
|
char padd = padding_by_zero ? '0' : ' ';
|
|
if (left_align) {
|
|
s.insert(0, new_width - s.size(), padd);
|
|
} else {
|
|
s.append(new_width - s.size(), ' '); // cannot append 0 because its different number then
|
|
}
|
|
}
|
|
}
|
|
if (sign && is_positive) {
|
|
s.insert(0, 1, '+');
|
|
}
|
|
if (space_on_left) {
|
|
s.insert(0, 1, ' ');
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
std::string mini_sprintf(const std::string &format_str, const std::vector<MlValue> ¶meters) {
|
|
int arg_position = 0;
|
|
char c, c1;
|
|
std::string buf;
|
|
std::string output_str;
|
|
|
|
std::string::const_iterator si;
|
|
for (si = format_str.begin(); si != format_str.end(); ++si) {
|
|
c = *si;
|
|
|
|
// formatting directives
|
|
if (c == '%') {
|
|
if (++si >= format_str.end()) {
|
|
return output_str; // end of string, invalid % on last pos
|
|
}
|
|
|
|
c1 = *si;
|
|
switch (c1) {
|
|
case '%':
|
|
output_str += c1;
|
|
break;
|
|
case '-':
|
|
case '+':
|
|
case ' ':
|
|
case '#':
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
case '.':
|
|
case 'i':
|
|
case 'd':
|
|
case 'f':
|
|
case 's':
|
|
bool left_align = false;
|
|
bool sign = false;
|
|
bool space_on_left = false;
|
|
bool padding_by_zero = false;
|
|
int width = -1;
|
|
int precision = -1;
|
|
int length = -1;
|
|
// http://www.cplusplus.com/reference/cstdio/printf/
|
|
// https://www.menie.org/georges/embedded/small_printf_source_code.html
|
|
//
|
|
// %[flags][width][.precision][length]specifier
|
|
|
|
// flags: - + \_ # 0
|
|
while ((*si == '-' || *si == '+' || *si == ' ' || *si == '#' || *si == '0') && si < format_str.end()) {
|
|
switch (*si) {
|
|
case '-':
|
|
left_align = true;
|
|
break;
|
|
case '+':
|
|
sign = true;
|
|
break;
|
|
case ' ':
|
|
space_on_left = true;
|
|
break;
|
|
case '#':
|
|
break;
|
|
case '0':
|
|
padding_by_zero = true;
|
|
left_align = true;
|
|
break;
|
|
};
|
|
si++;
|
|
}
|
|
// width
|
|
if (si >= format_str.end())
|
|
return output_str; // invalid end of string
|
|
while (*si >= '0' && *si <= '9' && si < format_str.end()) {
|
|
if (width == -1) {
|
|
width = 0;
|
|
}
|
|
width = width * 10 + (*si - '0');
|
|
si++;
|
|
}
|
|
// precision
|
|
if (si >= format_str.end())
|
|
return output_str; // invalid end of string
|
|
if (*si == '.') {
|
|
precision = 0;
|
|
if (++si >= format_str.end())
|
|
return output_str; // invalid end of string
|
|
while (*si >= '0' && *si <= '9' && si < format_str.end()) {
|
|
precision = precision * 10 + (*si - '0');
|
|
si++;
|
|
}
|
|
}
|
|
// length
|
|
// specifier
|
|
if (si >= format_str.end())
|
|
return output_str; // invalid end of string
|
|
if (*si == 'i' || *si == 'd' || *si == 'f' || *si == 's') { // TODO more specifiers
|
|
std::string s = mini_sprintf_format(left_align, sign, space_on_left, padding_by_zero, width, precision, length, *si, parameters[arg_position]);
|
|
arg_position++;
|
|
output_str += s;
|
|
} else {
|
|
output_str += "UNKNOWN FORMAT SPECIFIER";
|
|
}
|
|
};
|
|
|
|
// escaping sequences
|
|
} else if (c == '\\') {
|
|
if (++si >= format_str.end()) {
|
|
return output_str; // end of string, invalid % on last pos
|
|
}
|
|
|
|
c1 = *si;
|
|
switch (c1) {
|
|
case 't':
|
|
output_str += '\t';
|
|
break;
|
|
case 'r':
|
|
output_str += '\r';
|
|
break;
|
|
case 'n':
|
|
output_str += '\n';
|
|
break;
|
|
case 'x': // hex ie \x1b
|
|
if (si + 2 >= format_str.end()) return output_str; // end of string, invalid hex constant
|
|
buf.clear();
|
|
buf.push_back(*(si + 1));
|
|
buf.push_back(*(si + 2));
|
|
output_str.push_back((char)std::strtol( &buf[0], 0, 16));
|
|
si += 2;
|
|
break;
|
|
case '0': // octal ie "\033"
|
|
if (si + 2 >= format_str.end()) return output_str; // end of string, invalid octal constant
|
|
buf.clear();
|
|
buf.push_back(*(si + 1));
|
|
buf.push_back(*(si + 2));
|
|
output_str.push_back((char)std::strtol( &buf[0], 0, 8));
|
|
// TODO maybe octal constant has 3 bytes
|
|
// if (si + 2 >= format_str.end()) return output_str; // end of string, invalid octal or hex constant
|
|
// buf.push_back(*(si + 3));
|
|
// si += 3;
|
|
si += 2;
|
|
break;
|
|
default:
|
|
output_str += c1;
|
|
};
|
|
|
|
// normal characters
|
|
} else {
|
|
output_str += c;
|
|
}
|
|
}
|
|
|
|
return output_str;
|
|
} |