/* #includes */ /*{{{C}}}*//*{{{*/ #undef _POSIX_SOURCE #define _POSIX_SOURCE 1 #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 2 #include "config.h" #include #include #include #include #ifdef HAVE_GETTEXT #include #define _(String) gettext(String) #else #define _(String) String #endif #include #include /* Buggy on some systems */ #if 0 #ifdef HAVE_TGMATH_H #include #endif #else extern long int lrint(double x); #endif #include #include #include #include #include "error.h" #include "value.h" #ifdef USE_DMALLOC #include "dmalloc.h" #endif /*}}}*/ /* variables */ /*{{{*/ static const char *typestr[]= { (const char*)0, (const char*)0, "integer", (const char*)0, "real", "string", "void" }; /* for xgettext */ #if 0 _("integer") _("real") _("string") _("void") #endif /*}}}*/ const enum ValueType Value_commonType[V_VOID+1][V_VOID+1]= { { 0, 0, 0, 0, 0, 0, 0 }, { 0, V_ERROR, V_ERROR, V_ERROR, V_ERROR, V_ERROR, V_ERROR }, { 0, V_ERROR, V_INTEGER, V_ERROR, V_REAL, V_ERROR, V_ERROR }, { 0, V_ERROR, V_ERROR, V_ERROR, V_ERROR, V_ERROR, V_ERROR }, { 0, V_ERROR, V_REAL, V_ERROR, V_REAL, V_ERROR, V_ERROR }, { 0, V_ERROR, V_ERROR, V_ERROR, V_ERROR, V_STRING, V_ERROR }, { 0, V_ERROR, V_ERROR, V_ERROR, V_ERROR, V_ERROR, V_ERROR } }; #ifndef HAVE_LRINT long int lrint(double d) { return d; } #endif static void format_double(struct String *buf, double value, int width, int precision, int exponent) /*{{{*/ { if (exponent) { size_t len; char *e; int en; len=buf->length; String_appendPrintf(buf,"%.*E",width-1-(precision>=0),value); if (buf->character[len+1]=='.') String_delete(buf,len+1,1); if (precision>=0) String_insertChar(buf,len+width-precision-1,'.'); for (e=buf->character+buf->length-1; e>=buf->character && *e!='E'; --e); ++e; en=strtol(e,(char**)0,10); en=en+2-(width-precision); len=e-buf->character; String_delete(buf,len,buf->length-len); String_appendPrintf(buf,"%+0*d",exponent-1,en); } else if (precision>0) String_appendPrintf(buf,"%.*f",precision,value); else if (precision==0) String_appendPrintf(buf,"%.f.",value); else if (width) String_appendPrintf(buf,"%.f",value); else { double x=value; if (x<0.0001 || x>=10000000.0) /* print scientific notation */ { String_appendPrintf(buf,"%.7g",value); } else /* print decimal numbers or integers, if possible */ { int o,n,p=6; while (x>=10.0 && p>0) { x/=10.0; --p; } o=buf->length; String_appendPrintf(buf,"%.*f",p,value); n=buf->length; if (memchr(buf->character+o,'.',n-o)) { while (buf->character[buf->length-1]=='0') --buf->length; if (buf->character[buf->length-1]=='.') --buf->length; } } } } /*}}}*/ double Value_trunc(double d) /*{{{*/ { return (d<0.0?ceil(d):floor(d)); } /*}}}*/ double Value_round(double d) /*{{{*/ { return (d<0.0?ceil(d-0.5):floor(d+0.5)); } /*}}}*/ long int Value_toi(double d, int *overflow) /*{{{*/ { d=Value_round(d); *overflow=(dLONG_MAX); return lrint(d); } /*}}}*/ long int Value_vali(const char *s, char **end, int *overflow) /*{{{*/ { long int n; errno=0; if (*s=='&' && tolower(*(s+1))=='h') n=strtoul(s+2,end,16); else if (*s=='&' && tolower(*(s+1))=='o') n=strtoul(s+2,end,8); else n=strtol(s,end,10); *overflow=(errno==ERANGE); return n; } /*}}}*/ double Value_vald(const char *s, char **end, int *overflow) /*{{{*/ { double d; errno=0; d=strtod(s,end); *overflow=(errno==ERANGE); return d; } /*}}}*/ struct Value *Value_new_NIL(struct Value *this) /*{{{*/ { assert(this!=(struct Value*)0); this->type=V_NIL; return this; } /*}}}*/ struct Value *Value_new_ERROR(struct Value *this, int code, const char *error, ...) /*{{{*/ { va_list ap; char buf[128]; assert(this!=(struct Value*)0); va_start(ap,error); vsprintf(buf,error,ap); va_end(ap); this->type=V_ERROR; this->u.error.code=code; this->u.error.msg=strcpy(malloc(strlen(buf)+1),buf); return this; } /*}}}*/ struct Value *Value_new_INTEGER(struct Value *this, int n) /*{{{*/ { assert(this!=(struct Value*)0); this->type=V_INTEGER; this->u.integer=n; return this; } /*}}}*/ struct Value *Value_new_REAL(struct Value *this, double n) /*{{{*/ { assert(this!=(struct Value*)0); this->type=V_REAL; this->u.real=n; return this; } /*}}}*/ struct Value *Value_new_STRING(struct Value *this) /*{{{*/ { assert(this!=(struct Value*)0); this->type=V_STRING; String_new(&this->u.string); return this; } /*}}}*/ struct Value *Value_new_VOID(struct Value *this) /*{{{*/ { assert(this!=(struct Value*)0); this->type=V_VOID; return this; } /*}}}*/ struct Value *Value_new_null(struct Value *this, enum ValueType type) /*{{{*/ { assert(this!=(struct Value*)0); switch (type) { case V_INTEGER: { this->type=V_INTEGER; this->u.integer=0; break; } case V_REAL: { this->type=V_REAL; this->u.real=0.0; break; } case V_STRING: { this->type=V_STRING; String_new(&this->u.string); break; } case V_VOID: { this->type=V_VOID; break; } default: assert(0); } return this; } /*}}}*/ int Value_isNull(const struct Value *this) /*{{{*/ { switch (this->type) { case V_INTEGER: return (this->u.integer==0); case V_REAL: return (this->u.real==0.0); case V_STRING: return (this->u.string.length==0); default: assert(0); } return -1; } /*}}}*/ void Value_destroy(struct Value *this) /*{{{*/ { assert(this!=(struct Value*)0); switch (this->type) { case V_ERROR: free(this->u.error.msg); break; case V_INTEGER: break; case V_NIL: break; case V_REAL: break; case V_STRING: String_destroy(&this->u.string); break; case V_VOID: break; default: assert(0); } this->type=0; } /*}}}*/ struct Value *Value_clone(struct Value *this, const struct Value *original) /*{{{*/ { assert(this!=(struct Value*)0); assert(original!=(struct Value*)0); switch (original->type) { case V_ERROR: { strcpy(this->u.error.msg=malloc(strlen(original->u.error.msg)+1),original->u.error.msg); this->u.error.code=original->u.error.code; break; } case V_INTEGER: this->u.integer=original->u.integer; break; case V_NIL: break; case V_REAL: this->u.real=original->u.real; break; case V_STRING: String_clone(&this->u.string,&original->u.string); break; default: assert(0); } this->type=original->type; return this; } /*}}}*/ struct Value *Value_uplus(struct Value *this, int calc) /*{{{*/ { switch (this->type) { case V_INTEGER: case V_REAL: { break; } case V_STRING: { Value_destroy(this); Value_new_ERROR(this,INVALIDUOPERAND); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_uneg(struct Value *this, int calc) /*{{{*/ { switch (this->type) { case V_INTEGER: { if (calc) this->u.integer=-this->u.integer; break; } case V_REAL: { if (calc) this->u.real=-this->u.real; break; } case V_STRING: { Value_destroy(this); Value_new_ERROR(this,INVALIDUOPERAND); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_unot(struct Value *this, int calc) /*{{{*/ { switch (this->type) { case V_INTEGER: { if (calc) this->u.integer=~this->u.integer; break; } case V_REAL: { Value_retype(this,V_INTEGER); if (calc) this->u.integer=~this->u.integer; break; } case V_STRING: { Value_destroy(this); Value_new_ERROR(this,INVALIDUOPERAND); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_add(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) this->u.integer+=x->u.integer; break; } case V_REAL: { VALUE_RETYPE(this,V_REAL); VALUE_RETYPE(x,V_REAL); if (calc) this->u.real+=x->u.real; break; } case V_STRING: { if (calc) String_appendString(&this->u.string,&x->u.string); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_sub(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) this->u.integer-=x->u.integer; break; } case V_REAL: { VALUE_RETYPE(this,V_REAL); VALUE_RETYPE(x,V_REAL); if (calc) this->u.real-=x->u.real; break; } case V_STRING: { Value_destroy(this); Value_new_ERROR(this,INVALIDOPERAND); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_mult(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) this->u.integer*=x->u.integer; break; } case V_REAL: { VALUE_RETYPE(this,V_REAL); VALUE_RETYPE(x,V_REAL); if (calc) this->u.real*=x->u.real; break; } case V_STRING: { Value_destroy(this); Value_new_ERROR(this,INVALIDOPERAND); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_div(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(this,V_REAL); VALUE_RETYPE(x,V_REAL); if (calc) { if (x->u.real==0) { Value_destroy(this); Value_new_ERROR(this,UNDEFINED,"Division by zero"); } else this->u.real/=x->u.real; } break; } case V_REAL: { VALUE_RETYPE(this,V_REAL); VALUE_RETYPE(x,V_REAL); if (calc) { if (x->u.real==0.0) { Value_destroy(this); Value_new_ERROR(this,UNDEFINED,"Division by zero"); } else this->u.real/=x->u.real; } break; } case V_STRING: { Value_destroy(this); Value_new_ERROR(this,INVALIDOPERAND); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_idiv(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) { if (x->u.integer==0) { Value_destroy(this); Value_new_ERROR(this,UNDEFINED,"Division by zero"); } else this->u.integer/=x->u.integer; } break; } case V_REAL: { VALUE_RETYPE(this,V_REAL); VALUE_RETYPE(x,V_REAL); if (calc) { if (x->u.real==0.0) { Value_destroy(this); Value_new_ERROR(this,UNDEFINED,"Division by zero"); } else this->u.real=Value_trunc(this->u.real/x->u.real); } break; } case V_STRING: { Value_destroy(this); Value_new_ERROR(this,INVALIDOPERAND); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_mod(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) { if (x->u.integer==0) { Value_destroy(this); Value_new_ERROR(this,UNDEFINED,"Modulo by zero"); } else this->u.integer%=x->u.integer; } break; } case V_REAL: { VALUE_RETYPE(this,V_REAL); VALUE_RETYPE(x,V_REAL); if (calc) { if (x->u.real==0.0) { Value_destroy(this); Value_new_ERROR(this,UNDEFINED,"Modulo by zero"); } else this->u.real=fmod(this->u.real,x->u.real); } break; } case V_STRING: { Value_destroy(this); Value_new_ERROR(this,INVALIDOPERAND); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_pow(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) { if (this->u.integer==0 && x->u.integer==0) { Value_destroy(this); Value_new_ERROR(this,UNDEFINED,"0^0"); } else if (x->u.integer>0) this->u.integer=pow(this->u.integer,x->u.integer); else { long int thisi=this->u.integer; Value_destroy(this); Value_new_REAL(this,pow(thisi,x->u.integer)); } } break; } case V_REAL: { VALUE_RETYPE(this,V_REAL); VALUE_RETYPE(x,V_REAL); if (calc) { if (this->u.real==0.0 && x->u.real==0.0) { Value_destroy(this); Value_new_ERROR(this,UNDEFINED,"0^0"); } else this->u.real=pow(this->u.real,x->u.real); } break; } case V_STRING: { Value_destroy(this); Value_new_ERROR(this,INVALIDOPERAND); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_and(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: case V_REAL: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) this->u.integer&=x->u.integer; break; } case V_STRING: { Value_destroy(this); Value_new_ERROR(this,INVALIDOPERAND); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_or(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: case V_REAL: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) this->u.integer|=x->u.integer; break; } case V_STRING: { Value_destroy(this); Value_new_ERROR(this,INVALIDOPERAND); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_xor(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: case V_REAL: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) this->u.integer^=x->u.integer; break; } case V_STRING: { Value_destroy(this); Value_new_ERROR(this,INVALIDOPERAND); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_eqv(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: case V_REAL: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) this->u.integer=~(this->u.integer^x->u.integer); break; } case V_STRING: { Value_destroy(this); Value_new_ERROR(this,INVALIDOPERAND); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_imp(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: case V_REAL: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) this->u.integer=(~this->u.integer)|x->u.integer; break; } case V_STRING: { Value_destroy(this); Value_new_ERROR(this,INVALIDOPERAND); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_lt(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) this->u.integer=(this->u.integeru.integer)?-1:0; break; } case V_REAL: { int v; VALUE_RETYPE(this,V_REAL); VALUE_RETYPE(x,V_REAL); if (calc) v=(this->u.realu.real)?-1:0; else v=0; Value_destroy(this); Value_new_INTEGER(this,v); break; } case V_STRING: { int v; if (calc) v=(String_cmp(&this->u.string,&x->u.string)<0)?-1:0; else v=0; Value_destroy(this); Value_new_INTEGER(this,v); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_le(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) this->u.integer=(this->u.integer<=x->u.integer)?-1:0; break; } case V_REAL: { int v; VALUE_RETYPE(this,V_REAL); VALUE_RETYPE(x,V_REAL); if (calc) v=(this->u.real<=x->u.real)?-1:0; else v=0; Value_destroy(this); Value_new_INTEGER(this,v); break; } case V_STRING: { int v; if (calc) v=(String_cmp(&this->u.string,&x->u.string)<=0)?-1:0; else v=0; Value_destroy(this); Value_new_INTEGER(this,v); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_eq(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) this->u.integer=(this->u.integer==x->u.integer)?-1:0; break; } case V_REAL: { int v; VALUE_RETYPE(this,V_REAL); VALUE_RETYPE(x,V_REAL); if (calc) v=(this->u.real==x->u.real)?-1:0; else v=0; Value_destroy(this); Value_new_INTEGER(this,v); break; } case V_STRING: { int v; if (calc) v=(String_cmp(&this->u.string,&x->u.string)==0)?-1:0; else v=0; Value_destroy(this); Value_new_INTEGER(this,v); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_ge(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) this->u.integer=(this->u.integer>=x->u.integer)?-1:0; break; } case V_REAL: { int v; VALUE_RETYPE(this,V_REAL); VALUE_RETYPE(x,V_REAL); if (calc) v=(this->u.real>=x->u.real)?-1:0; else v=0; Value_destroy(this); Value_new_INTEGER(this,v); break; } case V_STRING: { int v; if (calc) v=(String_cmp(&this->u.string,&x->u.string)>=0)?-1:0; else v=0; Value_destroy(this); Value_new_INTEGER(this,v); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_gt(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) this->u.integer=(this->u.integer>x->u.integer)?-1:0; break; } case V_REAL: { int v; VALUE_RETYPE(this,V_REAL); VALUE_RETYPE(x,V_REAL); if (calc) v=(this->u.real>x->u.real)?-1:0; else v=0; Value_destroy(this); Value_new_INTEGER(this,v); break; } case V_STRING: { int v; if (calc) v=(String_cmp(&this->u.string,&x->u.string)>0)?-1:0; else v=0; Value_destroy(this); Value_new_INTEGER(this,v); break; } default: assert(0); } return this; } /*}}}*/ struct Value *Value_ne(struct Value *this, struct Value *x, int calc) /*{{{*/ { switch (Value_commonType[this->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(this,V_INTEGER); VALUE_RETYPE(x,V_INTEGER); if (calc) this->u.integer=(this->u.integer!=x->u.integer)?-1:0; break; } case V_REAL: { int v; VALUE_RETYPE(this,V_REAL); VALUE_RETYPE(x,V_REAL); if (calc) v=(this->u.real!=x->u.real)?-1:0; else v=0; Value_destroy(this); Value_new_INTEGER(this,v); break; } case V_STRING: { int v; if (calc) v=String_cmp(&this->u.string,&x->u.string)?-1:0; else v=0; Value_destroy(this); Value_new_INTEGER(this,v); break; } default: assert(0); } return this; } /*}}}*/ int Value_exitFor(struct Value *this, struct Value *limit, struct Value *step) /*{{{*/ { switch (this->type) { case V_INTEGER: return ( step->u.integer<0 ? (this->u.integeru.integer) : (this->u.integer>limit->u.integer) ); case V_REAL: return ( step->u.real<0.0 ? (this->u.realu.real) : (this->u.real>limit->u.real) ); case V_STRING: return (String_cmp(&this->u.string,&limit->u.string)>0); default: assert(0); } return -1; } /*}}}*/ void Value_errorPrefix(struct Value *this, const char *prefix) /*{{{*/ { size_t prefixlen,msglen; assert(this->type==V_ERROR); prefixlen=strlen(prefix); msglen=strlen(this->u.error.msg); this->u.error.msg=realloc(this->u.error.msg,prefixlen+msglen+1); memmove(this->u.error.msg+prefixlen,this->u.error.msg,msglen); memcpy(this->u.error.msg,prefix,prefixlen); } /*}}}*/ void Value_errorSuffix(struct Value *this, const char *suffix) /*{{{*/ { size_t suffixlen,msglen; assert(this->type==V_ERROR); suffixlen=strlen(suffix); msglen=strlen(this->u.error.msg); this->u.error.msg=realloc(this->u.error.msg,suffixlen+msglen+1); memcpy(this->u.error.msg+msglen,suffix,suffixlen+1); } /*}}}*/ struct Value *Value_new_typeError(struct Value *this, enum ValueType t1, enum ValueType t2) /*{{{*/ { assert(typestr[t1]); assert(typestr[t2]); return Value_new_ERROR(this,TYPEMISMATCH1,_(typestr[t1]),_(typestr[t2])); } /*}}}*/ static void retypeError(struct Value *this, enum ValueType to) /*{{{*/ { enum ValueType thisType=this->type; assert(typestr[thisType]); assert(typestr[to]); Value_destroy(this); Value_new_ERROR(this,TYPEMISMATCH1,_(typestr[thisType]),_(typestr[to])); } /*}}}*/ struct Value *Value_retype(struct Value *this, enum ValueType type) /*{{{*/ { switch (this->type) { case V_INTEGER: { switch (type) { case V_INTEGER: break; case V_REAL: this->u.real=this->u.integer; this->type=type; break; case V_VOID: Value_destroy(this); Value_new_VOID(this); break; default: retypeError(this,type); break; } break; } case V_REAL: { int overflow; switch (type) { case V_INTEGER: { this->u.integer=Value_toi(this->u.real,&overflow); this->type=V_INTEGER; if (overflow) { Value_destroy(this); Value_new_ERROR(this,OUTOFRANGE,typestr[V_INTEGER]); } break; } case V_REAL: break; case V_VOID: Value_destroy(this); Value_new_VOID(this); break; default: retypeError(this,type); break; } break; } case V_STRING: { switch (type) { case V_STRING: break; case V_VOID: Value_destroy(this); Value_new_VOID(this); break; default: retypeError(this,type); break; } break; } case V_VOID: { switch (type) { case V_VOID: break; default: retypeError(this,type); } break; } case V_ERROR: break; default: assert(0); } return this; } /*}}}*/ struct String *Value_toString(struct Value *this, struct String *s, char pad, int headingsign, size_t width, int commas, int dollar, int dollarleft, int precision, int exponent, int trailingsign) /*{{{*/ { size_t oldlength=s->length; switch (this->type) { case V_ERROR: String_appendChars(s,this->u.error.msg); break; case V_REAL: case V_INTEGER: { int sign; struct String buf; size_t totalwidth=width; String_new(&buf); if (this->type==V_INTEGER) { if (this->u.integer<0) { sign=-1; this->u.integer=-this->u.integer; } else if (this->u.integer==0) sign=0; else sign=1; } else { if (this->u.real<0.0) { sign=-1; this->u.real=-this->u.real; } else if (this->u.real==0.0) sign=0; else sign=1; } switch (headingsign) { case -1: { ++totalwidth; String_appendChar(&buf,sign==-1?'-':' '); break; } case 0: { if (sign==-1) String_appendChar(&buf,'-'); break; } case 1: { ++totalwidth; String_appendChar(&buf,sign==-1?'-':'+'); break; } case 2: break; default: assert(0); } totalwidth+=exponent; if (this->type==V_INTEGER) { if (precision>0 || exponent) format_double(&buf,(double)this->u.integer,width,precision,exponent); else if (precision==0) String_appendPrintf(&buf,"%lu.",this->u.integer); else String_appendPrintf(&buf,"%lu",this->u.integer); } else format_double(&buf,this->u.real,width,precision,exponent); if (commas) { size_t digits; int first; first=(headingsign?1:0); for (digits=first; digits='0' && buf.character[digits]<='9'; ++digits); while (digits>first+3) { digits-=3; String_insertChar(&buf,digits,','); } } if (dollar) { String_insertChar(&buf,0,'$'); } if (trailingsign==-1) { ++totalwidth; String_appendChar(&buf,sign==-1?'-':' '); } else if (trailingsign==1) { ++totalwidth; String_appendChar(&buf,sign==-1?'-':'+'); } String_size(s,oldlength+(totalwidth>buf.length?totalwidth:buf.length)); if (totalwidth>buf.length) memset(s->character+oldlength,pad,totalwidth-buf.length+dollarleft); memcpy(s->character+oldlength+(totalwidth>buf.length?(totalwidth-buf.length):0)+dollarleft,buf.character+dollarleft,buf.length-dollarleft); if (dollarleft) s->character[oldlength]='$'; String_destroy(&buf); break; } case V_STRING: { if (width>0) { size_t blanks=(this->u.string.lengthu.string.length):0); String_size(s,oldlength+width); memcpy(s->character+oldlength,this->u.string.character,blanks?this->u.string.length:width); if (blanks) memset(s->character+oldlength+this->u.string.length,' ',blanks); } else String_appendString(s,&this->u.string); break; } default: assert(0); return 0; } return s; } /*}}}*/ struct Value *Value_toStringUsing(struct Value *this, struct String *s, struct String *using, size_t *usingpos) /*{{{*/ { char pad=' '; int headingsign; int width=0; int commas=0; int dollar=0; int dollarleft=0; int precision=-1; int exponent=0; int trailingsign=0; headingsign=(using->length ? 0 : -1); if (*usingpos==using->length) *usingpos=0; while (*usingposlength) { switch (using->character[*usingpos]) { case '_': /* output next char */ /*{{{*/ { ++(*usingpos); if (*usingposlength) String_appendChar(s,using->character[(*usingpos)++]); else { Value_destroy(this); return Value_new_ERROR(this,MISSINGCHARACTER); } break; } /*}}}*/ case '!': /* output first character of string */ /*{{{*/ { width=1; ++(*usingpos); goto work; } /*}}}*/ case '\\': /* output n characters of string */ /*{{{*/ { width=1; ++(*usingpos); while (*usingposlength && using->character[*usingpos]==' ') { ++(*usingpos); ++width; } if (*usingposlength && using->character[*usingpos]=='\\') { ++(*usingpos); ++width; goto work; } else { Value_destroy(this); return Value_new_ERROR(this,IOERROR,_("unpaired \\ in format")); } break; } /*}}}*/ case '&': /* output string */ /*{{{*/ { width=0; ++(*usingpos); goto work; } /*}}}*/ case '*': case '$': case '0': case '+': case '#': case '.': { if (using->character[*usingpos]=='+') { headingsign=1; ++(*usingpos); } while (*usingposlength && strchr("$#*0,",using->character[*usingpos])) { switch (using->character[*usingpos]) { case '$': if (width==0) dollarleft=1; if (++dollar>1) ++width; break; case '*': pad='*'; ++width; break; case '0': pad='0'; ++width; break; case ',': commas=1; ++width; break; default: ++width; } ++(*usingpos); } if (*usingposlength && using->character[*usingpos]=='.' ) { ++(*usingpos); ++width; precision=0; while (*usingposlength && strchr("*#",using->character[*usingpos])) { ++(*usingpos); ++precision; ++width; } if (width==1 && precision==0) { Value_destroy(this); return Value_new_ERROR(this,BADFORMAT); } } if (*usingposlength && using->character[*usingpos]=='-' ) { ++(*usingpos); if (headingsign==0) headingsign=2; trailingsign=-1; } else if (*usingposlength && using->character[*usingpos]=='+') { ++(*usingpos); if (headingsign==0) headingsign=2; trailingsign=1; } while (*usingposlength && using->character[*usingpos]=='^') { ++(*usingpos); ++exponent; } goto work; } default: { String_appendChar(s,using->character[(*usingpos)++]); } } } work: Value_toString(this,s,pad,headingsign,width,commas,dollar,dollarleft,precision,exponent,trailingsign); if ((this->type==V_INTEGER || this->type==V_REAL) && width==0 && precision==-1) String_appendChar(s,' '); while (*usingposlength) { switch (using->character[*usingpos]) { case '_': /* output next char */ /*{{{*/ { ++(*usingpos); if (*usingposlength) String_appendChar(s,using->character[(*usingpos)++]); else { Value_destroy(this); return Value_new_ERROR(this,MISSINGCHARACTER); } break; } /*}}}*/ case '!': case '\\': case '&': case '*': case '0': case '+': case '#': case '.': return this; default: { String_appendChar(s,using->character[(*usingpos)++]); } } } return this; } /*}}}*/ struct String *Value_toWrite(struct Value *this, struct String *s) /*{{{*/ { switch (this->type) { case V_INTEGER: String_appendPrintf(s,"%ld",this->u.integer); break; case V_REAL: { double x; int p=DBL_DIG; int n,o; x=(this->u.real<0.0 ? -this->u.real : this->u.real); while (x>1.0 && p>0) { x/=10.0; --p; } o=s->length; String_appendPrintf(s,"%.*f",p,this->u.real); n=s->length; if (memchr(s->character+o,'.',n-o)) { while (s->character[s->length-1]=='0') --s->length; if (s->character[s->length-1]=='.') --s->length; } break; } case V_STRING: /*{{{*/ { size_t l=this->u.string.length; char *data=this->u.string.character; String_appendChar(s,'"'); while (l--) { if (*data=='"') String_appendChar(s,'"'); String_appendChar(s,*data); ++data; } String_appendChar(s,'"'); break; } /*}}}*/ default: assert(0); } return s; } /*}}}*/ struct Value *Value_nullValue(enum ValueType type) /*{{{*/ { static struct Value integer={ V_INTEGER }; static struct Value real={ V_REAL }; static struct Value string={ V_STRING }; static char n[]=""; static int init=0; if (!init) { integer.u.integer=0; real.u.real=0.0; string.u.string.length=0; string.u.string.character=n; } switch (type) { case V_INTEGER: return &integer; case V_REAL: return ℜ case V_STRING: return &string; default: assert(0); } return (struct Value*)0; } /*}}}*/