/**************************************************************************** * apps/interpreters/bas/bas_value.c * * Copyright (c) 1999-2014 Michael Haardt * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "bas_error.h" #include "bas_value.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define _(String) String /**************************************************************************** * Private Data ****************************************************************************/ static const char *typestr[] = { (const char *)0, (const char *)0, "integer", (const char *)0, "real", "string", "void" }; /* for xgettext */ 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 } }; /**************************************************************************** * Private Functions ****************************************************************************/ 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; int n; int 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; } } } } } /**************************************************************************** * Public Functions ****************************************************************************/ 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 = (d < LONG_MIN || d > LONG_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 *self) { assert(self != (struct Value *)0); self->type = V_NIL; return self; } struct Value *Value_new_ERROR(struct Value *self, int code, const char *error, ...) { va_list ap; char buf[128]; assert(self != (struct Value *)0); va_start(ap, error); vsprintf(buf, error, ap); va_end(ap); self->type = V_ERROR; self->u.error.code = code; self->u.error.msg = strdup(buf); return self; } struct Value *Value_new_INTEGER(struct Value *self, int n) { assert(self != (struct Value *)0); self->type = V_INTEGER; self->u.integer = n; return self; } struct Value *Value_new_REAL(struct Value *self, double n) { assert(self != (struct Value *)0); self->type = V_REAL; self->u.real = n; return self; } struct Value *Value_new_STRING(struct Value *self) { assert(self != (struct Value *)0); self->type = V_STRING; String_new(&self->u.string); return self; } struct Value *Value_new_VOID(struct Value *self) { assert(self != (struct Value *)0); self->type = V_VOID; return self; } struct Value *Value_new_null(struct Value *self, enum ValueType type) { assert(self != (struct Value *)0); switch (type) { case V_INTEGER: { self->type = V_INTEGER; self->u.integer = 0; break; } case V_REAL: { self->type = V_REAL; self->u.real = 0.0; break; } case V_STRING: { self->type = V_STRING; String_new(&self->u.string); break; } case V_VOID: { self->type = V_VOID; break; } default: assert(0); } return self; } int Value_isNull(const struct Value *self) { switch (self->type) { case V_INTEGER: return (self->u.integer == 0); case V_REAL: return (self->u.real == 0.0); case V_STRING: return (self->u.string.length == 0); default: assert(0); } return -1; } void Value_destroy(struct Value *self) { assert(self != (struct Value *)0); switch (self->type) { case V_ERROR: free(self->u.error.msg); break; case V_INTEGER: break; case V_NIL: break; case V_REAL: break; case V_STRING: String_destroy(&self->u.string); break; case V_VOID: break; default: assert(0); } self->type = 0; } struct Value *Value_clone(struct Value *self, const struct Value *original) { assert(self != (struct Value *)0); assert(original != (struct Value *)0); switch (original->type) { case V_ERROR: { self->u.error.msg = strdup(original->u.error.msg); self->u.error.code = original->u.error.code; break; } case V_INTEGER: self->u.integer = original->u.integer; break; case V_NIL: break; case V_REAL: self->u.real = original->u.real; break; case V_STRING: String_clone(&self->u.string, &original->u.string); break; default: assert(0); } self->type = original->type; return self; } struct Value *Value_uplus(struct Value *self, int calc) { switch (self->type) { case V_INTEGER: case V_REAL: { break; } case V_STRING: { Value_destroy(self); Value_new_ERROR(self, INVALIDUOPERAND); break; } default: assert(0); } return self; } struct Value *Value_uneg(struct Value *self, int calc) { switch (self->type) { case V_INTEGER: { if (calc) { self->u.integer = -self->u.integer; } break; } case V_REAL: { if (calc) { self->u.real = -self->u.real; } break; } case V_STRING: { Value_destroy(self); Value_new_ERROR(self, INVALIDUOPERAND); break; } default: assert(0); } return self; } struct Value *Value_unot(struct Value *self, int calc) { switch (self->type) { case V_INTEGER: { if (calc) { self->u.integer = ~self->u.integer; } break; } case V_REAL: { Value_retype(self, V_INTEGER); if (calc) { self->u.integer = ~self->u.integer; } break; } case V_STRING: { Value_destroy(self); Value_new_ERROR(self, INVALIDUOPERAND); break; } default: assert(0); } return self; } struct Value *Value_add(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { self->u.integer += x->u.integer; } break; } case V_REAL: { VALUE_RETYPE(self, V_REAL); VALUE_RETYPE(x, V_REAL); if (calc) { self->u.real += x->u.real; } break; } case V_STRING: { if (calc) { String_appendString(&self->u.string, &x->u.string); } break; } default: assert(0); } return self; } struct Value *Value_sub(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { self->u.integer -= x->u.integer; } break; } case V_REAL: { VALUE_RETYPE(self, V_REAL); VALUE_RETYPE(x, V_REAL); if (calc) { self->u.real -= x->u.real; } break; } case V_STRING: { Value_destroy(self); Value_new_ERROR(self, INVALIDOPERAND); break; } default: assert(0); } return self; } struct Value *Value_mult(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { self->u.integer *= x->u.integer; } break; } case V_REAL: { VALUE_RETYPE(self, V_REAL); VALUE_RETYPE(x, V_REAL); if (calc) { self->u.real *= x->u.real; } break; } case V_STRING: { Value_destroy(self); Value_new_ERROR(self, INVALIDOPERAND); break; } default: assert(0); } return self; } struct Value *Value_div(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(self, V_REAL); VALUE_RETYPE(x, V_REAL); if (calc) { if (x->u.real == 0) { Value_destroy(self); Value_new_ERROR(self, UNDEFINED, "Division by zero"); } else { self->u.real /= x->u.real; } } break; } case V_REAL: { VALUE_RETYPE(self, V_REAL); VALUE_RETYPE(x, V_REAL); if (calc) { if (x->u.real == 0.0) { Value_destroy(self); Value_new_ERROR(self, UNDEFINED, "Division by zero"); } else { self->u.real /= x->u.real; } } break; } case V_STRING: { Value_destroy(self); Value_new_ERROR(self, INVALIDOPERAND); break; } default: assert(0); } return self; } struct Value *Value_idiv(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { if (x->u.integer == 0) { Value_destroy(self); Value_new_ERROR(self, UNDEFINED, "Division by zero"); } else { self->u.integer /= x->u.integer; } } break; } case V_REAL: { VALUE_RETYPE(self, V_REAL); VALUE_RETYPE(x, V_REAL); if (calc) { if (x->u.real == 0.0) { Value_destroy(self); Value_new_ERROR(self, UNDEFINED, "Division by zero"); } else { self->u.real = Value_trunc(self->u.real / x->u.real); } } break; } case V_STRING: { Value_destroy(self); Value_new_ERROR(self, INVALIDOPERAND); break; } default: assert(0); } return self; } struct Value *Value_mod(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { if (x->u.integer == 0) { Value_destroy(self); Value_new_ERROR(self, UNDEFINED, "Modulo by zero"); } else { self->u.integer %= x->u.integer; } } break; } case V_REAL: { VALUE_RETYPE(self, V_REAL); VALUE_RETYPE(x, V_REAL); if (calc) { if (x->u.real == 0.0) { Value_destroy(self); Value_new_ERROR(self, UNDEFINED, "Modulo by zero"); } else { self->u.real = fmod(self->u.real, x->u.real); } } break; } case V_STRING: { Value_destroy(self); Value_new_ERROR(self, INVALIDOPERAND); break; } default: assert(0); } return self; } struct Value *Value_pow(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { if (self->u.integer == 0 && x->u.integer == 0) { Value_destroy(self); Value_new_ERROR(self, UNDEFINED, "0^0"); } else if (x->u.integer > 0) { self->u.integer = pow(self->u.integer, x->u.integer); } else { long int thisi = self->u.integer; Value_destroy(self); Value_new_REAL(self, pow(thisi, x->u.integer)); } } break; } case V_REAL: { VALUE_RETYPE(self, V_REAL); VALUE_RETYPE(x, V_REAL); if (calc) { if (self->u.real == 0.0 && x->u.real == 0.0) { Value_destroy(self); Value_new_ERROR(self, UNDEFINED, "0^0"); } else { self->u.real = pow(self->u.real, x->u.real); } } break; } case V_STRING: { Value_destroy(self); Value_new_ERROR(self, INVALIDOPERAND); break; } default: assert(0); } return self; } struct Value *Value_and(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: case V_REAL: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { self->u.integer &= x->u.integer; } break; } case V_STRING: { Value_destroy(self); Value_new_ERROR(self, INVALIDOPERAND); break; } default: assert(0); } return self; } struct Value *Value_or(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: case V_REAL: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { self->u.integer |= x->u.integer; } break; } case V_STRING: { Value_destroy(self); Value_new_ERROR(self, INVALIDOPERAND); break; } default: assert(0); } return self; } struct Value *Value_xor(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: case V_REAL: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { self->u.integer ^= x->u.integer; } break; } case V_STRING: { Value_destroy(self); Value_new_ERROR(self, INVALIDOPERAND); break; } default: assert(0); } return self; } struct Value *Value_eqv(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: case V_REAL: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { self->u.integer = ~(self->u.integer ^ x->u.integer); } break; } case V_STRING: { Value_destroy(self); Value_new_ERROR(self, INVALIDOPERAND); break; } default: assert(0); } return self; } struct Value *Value_imp(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: case V_REAL: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { self->u.integer = (~self->u.integer) | x->u.integer; } break; } case V_STRING: { Value_destroy(self); Value_new_ERROR(self, INVALIDOPERAND); break; } default: assert(0); } return self; } struct Value *Value_lt(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { self->u.integer = (self->u.integer < x->u.integer) ? -1 : 0; } break; } case V_REAL: { int v; VALUE_RETYPE(self, V_REAL); VALUE_RETYPE(x, V_REAL); if (calc) { v = (self->u.real < x->u.real) ? -1 : 0; } else { v = 0; } Value_destroy(self); Value_new_INTEGER(self, v); break; } case V_STRING: { int v; if (calc) { v = (String_cmp(&self->u.string, &x->u.string) < 0) ? -1 : 0; } else { v = 0; } Value_destroy(self); Value_new_INTEGER(self, v); break; } default: assert(0); } return self; } struct Value *Value_le(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { self->u.integer = (self->u.integer <= x->u.integer) ? -1 : 0; } break; } case V_REAL: { int v; VALUE_RETYPE(self, V_REAL); VALUE_RETYPE(x, V_REAL); if (calc) { v = (self->u.real <= x->u.real) ? -1 : 0; } else { v = 0; } Value_destroy(self); Value_new_INTEGER(self, v); break; } case V_STRING: { int v; if (calc) { v = (String_cmp(&self->u.string, &x->u.string) <= 0) ? -1 : 0; } else { v = 0; } Value_destroy(self); Value_new_INTEGER(self, v); break; } default: assert(0); } return self; } struct Value *Value_eq(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { self->u.integer = (self->u.integer == x->u.integer) ? -1 : 0; } break; } case V_REAL: { int v; VALUE_RETYPE(self, V_REAL); VALUE_RETYPE(x, V_REAL); if (calc) { v = (self->u.real == x->u.real) ? -1 : 0; } else { v = 0; } Value_destroy(self); Value_new_INTEGER(self, v); break; } case V_STRING: { int v; if (calc) { v = (String_cmp(&self->u.string, &x->u.string) == 0) ? -1 : 0; } else { v = 0; } Value_destroy(self); Value_new_INTEGER(self, v); break; } default: assert(0); } return self; } struct Value *Value_ge(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { self->u.integer = (self->u.integer >= x->u.integer) ? -1 : 0; } break; } case V_REAL: { int v; VALUE_RETYPE(self, V_REAL); VALUE_RETYPE(x, V_REAL); if (calc) { v = (self->u.real >= x->u.real) ? -1 : 0; } else { v = 0; } Value_destroy(self); Value_new_INTEGER(self, v); break; } case V_STRING: { int v; if (calc) { v = (String_cmp(&self->u.string, &x->u.string) >= 0) ? -1 : 0; } else { v = 0; } Value_destroy(self); Value_new_INTEGER(self, v); break; } default: assert(0); } return self; } struct Value *Value_gt(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { self->u.integer = (self->u.integer > x->u.integer) ? -1 : 0; } break; } case V_REAL: { int v; VALUE_RETYPE(self, V_REAL); VALUE_RETYPE(x, V_REAL); if (calc) { v = (self->u.real > x->u.real) ? -1 : 0; } else { v = 0; } Value_destroy(self); Value_new_INTEGER(self, v); break; } case V_STRING: { int v; if (calc) { v = (String_cmp(&self->u.string, &x->u.string) > 0) ? -1 : 0; } else { v = 0; } Value_destroy(self); Value_new_INTEGER(self, v); break; } default: assert(0); } return self; } struct Value *Value_ne(struct Value *self, struct Value *x, int calc) { switch (Value_commonType[self->type][x->type]) { case V_INTEGER: { VALUE_RETYPE(self, V_INTEGER); VALUE_RETYPE(x, V_INTEGER); if (calc) { self->u.integer = (self->u.integer != x->u.integer) ? -1 : 0; } break; } case V_REAL: { int v; VALUE_RETYPE(self, V_REAL); VALUE_RETYPE(x, V_REAL); if (calc) { v = (self->u.real != x->u.real) ? -1 : 0; } else { v = 0; } Value_destroy(self); Value_new_INTEGER(self, v); break; } case V_STRING: { int v; if (calc) { v = String_cmp(&self->u.string, &x->u.string) ? -1 : 0; } else { v = 0; } Value_destroy(self); Value_new_INTEGER(self, v); break; } default: assert(0); } return self; } int Value_exitFor(struct Value *self, struct Value *limit, struct Value *step) { switch (self->type) { case V_INTEGER: return (step->u.integer < 0 ? (self->u.integer < limit->u.integer) : (self->u.integer > limit->u.integer)); case V_REAL: return (step->u.real < 0.0 ? (self->u.real < limit->u.real) : (self->u.real > limit->u.real)); case V_STRING: return (String_cmp(&self->u.string, &limit->u.string) > 0); default: assert(0); } return -1; } void Value_errorPrefix(struct Value *self, const char *prefix) { size_t prefixlen; size_t msglen; assert(self->type == V_ERROR); prefixlen = strlen(prefix); msglen = strlen(self->u.error.msg); self->u.error.msg = realloc(self->u.error.msg, prefixlen + msglen + 1); memmove(self->u.error.msg + prefixlen, self->u.error.msg, msglen); memcpy(self->u.error.msg, prefix, prefixlen); } void Value_errorSuffix(struct Value *self, const char *suffix) { size_t suffixlen; size_t msglen; assert(self->type == V_ERROR); suffixlen = strlen(suffix); msglen = strlen(self->u.error.msg); self->u.error.msg = realloc(self->u.error.msg, suffixlen + msglen + 1); memcpy(self->u.error.msg + msglen, suffix, suffixlen + 1); } struct Value *Value_new_typeError(struct Value *self, enum ValueType t1, enum ValueType t2) { assert(typestr[t1]); assert(typestr[t2]); return Value_new_ERROR(self, TYPEMISMATCH1, _(typestr[t1]), _(typestr[t2])); } static void retypeError(struct Value *self, enum ValueType to) { enum ValueType thisType = self->type; assert(typestr[thisType]); assert(typestr[to]); Value_destroy(self); Value_new_ERROR(self, TYPEMISMATCH1, _(typestr[thisType]), _(typestr[to])); } struct Value *Value_retype(struct Value *self, enum ValueType type) { switch (self->type) { case V_INTEGER: { switch (type) { case V_INTEGER: break; case V_REAL: self->u.real = self->u.integer; self->type = type; break; case V_VOID: Value_destroy(self); Value_new_VOID(self); break; default: retypeError(self, type); break; } break; } case V_REAL: { int overflow; switch (type) { case V_INTEGER: { self->u.integer = Value_toi(self->u.real, &overflow); self->type = V_INTEGER; if (overflow) { Value_destroy(self); Value_new_ERROR(self, OUTOFRANGE, typestr[V_INTEGER]); } break; } case V_REAL: break; case V_VOID: Value_destroy(self); Value_new_VOID(self); break; default: retypeError(self, type); break; } break; } case V_STRING: { switch (type) { case V_STRING: break; case V_VOID: Value_destroy(self); Value_new_VOID(self); break; default: retypeError(self, type); break; } break; } case V_VOID: { switch (type) { case V_VOID: break; default: retypeError(self, type); } break; } case V_ERROR: break; default: assert(0); } return self; } struct String *Value_toString(struct Value *self, 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 (self->type) { case V_ERROR: String_appendChars(s, self->u.error.msg); break; case V_REAL: case V_INTEGER: { int sign; struct String buf; size_t totalwidth = width; String_new(&buf); if (self->type == V_INTEGER) { if (self->u.integer < 0) { sign = -1; self->u.integer = -self->u.integer; } else if (self->u.integer == 0) { sign = 0; } else { sign = 1; } } else { if (self->u.real < 0.0) { sign = -1; self->u.real = -self->u.real; } else if (self->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 (self->type == V_INTEGER) { if (precision > 0 || exponent) { format_double(&buf, (double)self->u.integer, width, precision, exponent); } else if (precision == 0) { String_appendPrintf(&buf, "%lu.", self->u.integer); } else { String_appendPrintf(&buf, "%lu", self->u.integer); } } else { format_double(&buf, self->u.real, width, precision, exponent); } if (commas) { size_t digits; int first; first = (headingsign ? 1 : 0); for (digits = first; digits < buf.length && buf.character[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 = (self->u.string.length < width ? (width - self->u.string.length) : 0); String_size(s, oldlength + width); memcpy(s->character + oldlength, self->u.string.character, blanks ? self->u.string.length : width); if (blanks) { memset(s->character + oldlength + self->u.string.length, ' ', blanks); } } else { String_appendString(s, &self->u.string); } break; } default: assert(0); return 0; } return s; } struct Value *Value_toStringUsing(struct Value *self, 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 (*usingpos < using->length) { switch (using->character[*usingpos]) { case '_': /* output next char */ { ++(*usingpos); if (*usingpos < using->length) { String_appendChar(s, using->character[(*usingpos)++]); } else { Value_destroy(self); return Value_new_ERROR(self, MISSINGCHARACTER); } break; } case '!': /* output first character of string */ { width = 1; ++(*usingpos); goto work; } case '\\': /* output n characters of string */ { width = 1; ++(*usingpos); while (*usingpos < using->length && using->character[*usingpos] == ' ') { ++(*usingpos); ++width; } if (*usingpos < using->length && using->character[*usingpos] == '\\') { ++(*usingpos); ++width; goto work; } else { Value_destroy(self); return Value_new_ERROR(self, 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 (*usingpos < using->length && 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 (*usingpos < using->length && using->character[*usingpos] == '.') { ++(*usingpos); ++width; precision = 0; while (*usingpos < using->length && strchr("*#", using->character[*usingpos])) { ++(*usingpos); ++precision; ++width; } if (width == 1 && precision == 0) { Value_destroy(self); return Value_new_ERROR(self, BADFORMAT); } } if (*usingpos < using->length && using->character[*usingpos] == '-') { ++(*usingpos); if (headingsign == 0) { headingsign = 2; } trailingsign = -1; } else if (*usingpos < using->length && using->character[*usingpos] == '+') { ++(*usingpos); if (headingsign == 0) { headingsign = 2; } trailingsign = 1; } while (*usingpos < using->length && using->character[*usingpos] == '^') { ++(*usingpos); ++exponent; } goto work; } default: { String_appendChar(s, using->character[(*usingpos)++]); } } } work: Value_toString(self, s, pad, headingsign, width, commas, dollar, dollarleft, precision, exponent, trailingsign); if ((self->type == V_INTEGER || self->type == V_REAL) && width == 0 && precision == -1) { String_appendChar(s, ' '); } while (*usingpos < using->length) { switch (using->character[*usingpos]) { case '_': /* output next char */ { ++(*usingpos); if (*usingpos < using->length) { String_appendChar(s, using->character[(*usingpos)++]); } else { Value_destroy(self); return Value_new_ERROR(self, MISSINGCHARACTER); } break; } case '!': case '\\': case '&': case '*': case '0': case '+': case '#': case '.': return self; default: { String_appendChar(s, using->character[(*usingpos)++]); } } } return self; } struct String *Value_toWrite(struct Value *self, struct String *s) { switch (self->type) { case V_INTEGER: String_appendPrintf(s, "%ld", self->u.integer); break; case V_REAL: { double x; int p = DBL_DIG; int n; int o; x = (self->u.real < 0.0 ? -self->u.real : self->u.real); while (x > 1.0 && p > 0) { x /= 10.0; --p; } o = s->length; String_appendPrintf(s, "%.*f", p, self->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 = self->u.string.length; char *data = self->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 int init = 0; if (!init) { integer.u.integer = 0; real.u.real = 0.0; string.u.string.length = 0; string.u.string.character = ""; init = 1; } switch (type) { case V_INTEGER: return &integer; case V_REAL: return ℜ case V_STRING: return &string; default: assert(0); } return (struct Value *)0; } long int lrint(double d) { return d; }