mlisp/clib/printf.cpp

222 lines
5.3 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 == 'c') {
std::ostringstream stream_str;
stream_str << (char) value.as_int();
return stream_str.str();
}
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> &parameters) {
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 'c':
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' || *si == 'c') { // 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;
}