/*****
* runstring.in
*
* Runtime functions for string operations.
*
*****/
stringarray2* => stringArray2()
#include <cfloat>
#include <cstring>
#include <iomanip>
#include <ctime>
#include <chrono>
#include <algorithm>
#include "array.h"
using namespace camp;
using namespace vm;
using namespace settings;
typedef array stringarray;
typedef array stringarray2;
using types::stringArray;
using types::stringArray2;
namespace types {
extern const char *names[];
}
namespace run {
extern string emptystring;
}
static const string defaulttimeformat=string("%a %b %d %T %Z %Y");
#ifdef HAVE_STRFTIME
static const size_t nTime=256;
static char Time[nTime];
#endif
void checkformat(const char *ptr, bool intformat)
{
while(*ptr != '\0') {
if(*ptr != '%') /* While we have regular characters, print them. */
ptr++;
else { /* We've got a format specifier. */
ptr++;
while(*ptr && strchr ("-+ #0'I", *ptr)) /* Move past flags. */
ptr++;
if(*ptr == '*')
ptr++;
else while(isdigit(*ptr)) /* Handle explicit numeric value. */
ptr++;
if(*ptr == '.') {
ptr++; /* Go past the period. */
if(*ptr == '*') {
ptr++;
} else
while(isdigit(*ptr)) /* Handle explicit numeric value. */
ptr++;
}
while(*ptr && strchr ("hlL", *ptr))
ptr++;
if(*ptr == '%') {++ptr; continue;}
else if(*ptr != '\0') {
if(intformat) {
switch(*ptr) {
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X':
case 'c':
break;
default:
ostringstream buf;
buf << "Invalid format '" << *ptr << "' for type "
<< types::names[types::ty_Int];
error(buf);
break;
}
} else {
switch(*ptr) {
case 'f':
case 'F':
case 'e':
case 'E':
case 'g':
case 'G':
break;
default:
ostringstream buf;
buf << "Invalid format '" << *ptr << "' for type "
<< types::names[types::ty_real];
error(buf);
break;
}
}
}
break; // Only one argument is allowed.
} /* End of else statement */
}
}
// Autogenerated routines:
// String operations
string :emptyString()
{
return emptystring;
}
Int length(string *s)
{
return (Int) s->length();
}
Int find(string *s, string t, Int pos=0)
{
size_t n=s->find(t,pos);
return n == string::npos ? (Int) -1 : (Int) n;
}
Int rfind(string *s, string t, Int pos=-1)
{
size_t n=s->rfind(t,pos);
return n == string::npos ? (Int) -1 : (Int) n;
}
string reverse(string s)
{
reverse(s.begin(),s.end());
return s;
}
string insert(string s, Int pos, string t)
{
if ((size_t) pos < s.length())
return s.insert(pos,t);
return s;
}
string substr(string* s, Int pos, Int n=-1)
{
if ((size_t) pos < s->length())
return s->substr(pos,n);
return emptystring;
}
string erase(string s, Int pos, Int n)
{
if ((size_t) pos < s.length())
return s.erase(pos,n);
return s;
}
string downcase(string s)
{
std::transform(s.begin(),s.end(),s.begin(),tolower);
return s;
}
string upcase(string s)
{
std::transform(s.begin(),s.end(),s.begin(),toupper);
return s;
}
// returns a string constructed by translating all occurrences of the string
// from in an array of string pairs {from,to} to the string to in string s.
string replace(string *S, stringarray2 *translate)
{
size_t size=checkArray(translate);
for(size_t i=0; i < size; i++) {
array *a=read<array*>(translate,i);
checkArray(a);
}
size_t pos=0;
ostringstream buf;
size_t Len=S->length();
while(pos < Len) {
for(size_t i=0; i < size;) {
array *a=read<array*>(translate,i);
size_t size2=checkArray(a);
if(size2 != 2)
error("translation table entry must be an array of length 2");
string* from=read<string*>(a,0);
size_t len=from->length();
if(len == 0 || S->compare(pos,len,*from,0,len) != 0) {i++; continue;}
buf << read<string>(a,1);
pos += len;
if(pos == Len) return buf.str();
i=0;
}
buf << S->substr(pos,1);
++pos;
}
return buf.str();
}
string format(string *format, Int x, string locale=emptystring)
{
ostringstream out;
const char *p0=format->c_str();
checkformat(p0,true);
const char *p=p0;
const char *start=NULL;
while(*p != 0) {
char curr=*p;
if(curr == '%') {
p++;
if(*p != '%') {start=p-1; break;}
}
out << *(p++);
}
if(!start) return out.str();
// Allow at most 1 argument
while(*p != 0) {
if(*p == '*' || *p == '$') return out.str();
if(isupper(*p) || islower(*p)) {p++; break;}
p++;
}
string f=format->substr(start-p0,p-start);
f.insert(p-start-1,"ll");
const char *oldlocale=NULL;
if(!locale.empty()) {
oldlocale=setlocale(LC_ALL,NULL);
if(oldlocale) oldlocale=StrdupNoGC(oldlocale);
setlocale(LC_ALL,locale.c_str());
}
Int size=snprintf(NULL,0,f.c_str(),x)+1;
if(size < 1) size=255; // Workaround for non-C99 compliant systems.
char *buf=new char[size];
snprintf(buf,size,f.c_str(),x);
out << string(buf);
out << p;
delete[] buf;
if(oldlocale) {
setlocale(LC_ALL,oldlocale);
delete[] oldlocale;
}
return out.str();
}
string format(string *format, bool forcemath=false, string separator, real x,
string locale=emptystring)
{
if(*format == "%") return ""; // Temporary workaround for github Issue #29.
bool tex=getSetting<string>("tex") != "none";
bool texify=forcemath;
ostringstream out;
const char *p0=format->c_str();
checkformat(p0,false);
const char *phantom="\\phantom{+}";
const char *p=p0;
const char *start=NULL;
char prev=0;
while(*p != 0) {
char curr=*p;
if(tex && curr == '$' && prev != '\\') texify=true;
prev=curr;
if(curr == '%') {
p++;
if(*p != '%') {start=p-1; break;}
}
out << *(p++);
}
if(!start) return out.str();
// Allow at most 1 argument
while(*p != 0) {
if(*p == '*' || *p == '$') return out.str();
if(isupper(*p) || islower(*p)) {p++; break;}
p++;
}
const char *tail=p;
string f=format->substr(start-p0,tail-start);
const char *oldlocale=NULL;
if(!locale.empty()) {
oldlocale=setlocale(LC_ALL,NULL);
if(oldlocale) oldlocale=StrdupNoGC(oldlocale);
setlocale(LC_ALL,locale.c_str());
}
Int size=snprintf(NULL,0,f.c_str(),x)+1;
if(size < 1) size=255; // Workaround for non-C99 compliant systems.
char *buf=new char[size];
snprintf(buf,size,f.c_str(),x);
bool trailingzero=f.find("#") < string::npos;
bool plus=f.find("+") < string::npos;
bool space=f.find(" ") < string::npos;
char *q=buf; // beginning of formatted number
if(*q == ' ' && texify) {
out << phantom;
q++;
}
const char decimal=*(localeconv()->decimal_point);
// Remove any spurious sign
if(*q == '-' || *q == '+') {
p=q+1;
bool zero=true;
while(*p != 0) {
if(!isdigit(*p) && *p != decimal) break;
if(isdigit(*p) && *p != '0') {zero=false; break;}
p++;
}
if(zero) {
q++;
if((plus || space) && texify) out << phantom;
}
}
const char *r=p=q;
bool dp=false;
while(*r != 0 && (isspace(*r) || isdigit(*r) || *r == decimal \
|| *r == '+' || *r == '-')) {
if(*r == decimal) dp=true;
r++;
}
if(dp) { // Remove trailing zeros and/or decimal point
r--;
unsigned n=0;
while(r > q && *r == '0') {r--; n++;}
if(*r == decimal) {r--; n++;}
while(q <= r) out << *(q++);
if(!trailingzero) q += n;
}
bool zero=(r == p && *r == '0') && !trailingzero;
// Translate "E+/E-/e+/e-" exponential notation to TeX
while(*q != 0) {
if(texify && (*q == 'E' || *q == 'e') &&
(*(q+1) == '+' || *(q+1) == '-')) {
if(!zero) out << separator << "10^{";
bool plus=(*(q+1) == '+');
q++;
if(plus) q++;
if(*q == '-') out << *(q++);
while(*q == '0' && (zero || isdigit(*(q+1)))) q++;
while(isdigit(*q)) out << *(q++);
if(!zero)
out << "}";
break;
}
out << *(q++);
}
while(*tail != 0)
out << *(tail++);
delete[] buf;
if(oldlocale) {
setlocale(LC_ALL,oldlocale);
delete[] oldlocale;
}
return out.str();
}
Int hex(string s)
{
istringstream is(s);
is.setf(std::ios::hex,std::ios::basefield);
Int value;
if(is && is >> value && ((is >> std::ws).eof())) return value;
ostringstream buf;
buf << "invalid hexadecimal cast from string \"" << s << "\"";
error(buf);
}
Int ascii(string s)
{
return s.empty() ? -1 : (unsigned char) s[0];
}
string string(Int x)
{
ostringstream buf;
buf << x;
return buf.str();
}
string string(real x, Int digits=DBL_DIG)
{
ostringstream buf;
buf.precision(digits);
buf << x;
return buf.str();
}
string time(string format=defaulttimeformat)
{
#ifdef HAVE_STRFTIME
const time_t bintime=time(NULL);
if(!strftime(Time,nTime,format.c_str(),localtime(&bintime))) return "";
return Time;
#else
return format;
#endif
}
string time(Int seconds, string format=defaulttimeformat)
{
#ifdef HAVE_STRFTIME
const time_t bintime=seconds;
if(!strftime(Time,nTime,format.c_str(),localtime(&bintime))) return "";
return Time;
#else
// Avoid unused variable warning messages
unused(&seconds);
return format;
#endif
}
Int seconds(string t=emptystring, string format=emptystring)
{
if (t == "")
{
auto clock = std::chrono::system_clock::now();
return static_cast<Int>(
std::chrono::duration_cast<std::chrono::seconds>(
clock.time_since_epoch()
).count()
);
}
std::tm tmObj = {};
istringstream instream(t);
instream.imbue(std::locale(""));
instream >> std::get_time(&tmObj,format.c_str());
if(instream.fail())
{
return -1;
}
return static_cast<Int>(std::mktime(&tmObj));
}