mirror of
git://git.suckless.org/sbase
synced 2026-01-26 13:43:17 +00:00
Truncate() is a function since POSIX 2008, and even it should be exposed only if unistd.h is included, OpenBSD exposes it even when we are using only c90 headers. Renamed to numtrunc() to avoid the name collision.
2200 lines
30 KiB
C
2200 lines
30 KiB
C
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <setjmp.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
|
|
#include "arg.h"
|
|
#include "util.h"
|
|
|
|
#define NDIGITS 10
|
|
#define REGSIZ 16
|
|
#define HASHSIZ 128
|
|
|
|
enum {
|
|
NVAL,
|
|
STR,
|
|
NUM,
|
|
};
|
|
|
|
enum {
|
|
NE,
|
|
LE,
|
|
GE,
|
|
};
|
|
|
|
typedef struct num Num;
|
|
typedef struct val Val;
|
|
|
|
struct num {
|
|
int begin;
|
|
int scale;
|
|
int room;
|
|
signed char *buf, *wp, *rp;
|
|
};
|
|
|
|
struct digit {
|
|
int val;
|
|
struct digit *next;
|
|
};
|
|
|
|
struct val {
|
|
int type;
|
|
union {
|
|
Num *n;
|
|
char *s;
|
|
} u;
|
|
|
|
struct val *next;
|
|
};
|
|
|
|
struct ary {
|
|
int n;
|
|
Val *buf;
|
|
struct ary *next;
|
|
};
|
|
|
|
struct reg {
|
|
char *name;
|
|
Val val;
|
|
struct ary ary;
|
|
struct reg *next;
|
|
};
|
|
|
|
struct input {
|
|
FILE *fp;
|
|
char *buf;
|
|
size_t n;
|
|
char *s;
|
|
struct input *next;
|
|
};
|
|
|
|
static Val *stack;
|
|
static jmp_buf env;
|
|
static struct input *input;
|
|
static struct reg *htbl[HASHSIZ];
|
|
|
|
static signed char onestr[] = {1, 0};
|
|
static Num zero, one = {.buf = onestr, .wp = onestr + 1};
|
|
static char digits[] = "0123456789ABCDEF.";
|
|
|
|
static int scale, ibase = 10, obase = 10;
|
|
static int iflag;
|
|
static int col;
|
|
|
|
/*
|
|
* This dc implementation follows the decription of dc found in the paper
|
|
* DC - An Interactive Desk Calculator, by Robert Morris and Lorinda Cherry
|
|
*/
|
|
|
|
static void
|
|
dumpnum(char *msg, Num *num)
|
|
{
|
|
signed char *p;
|
|
|
|
printf("Num (%s)", msg ? msg : "");
|
|
|
|
if (!num) {
|
|
puts("NULL\n");
|
|
return;
|
|
}
|
|
printf("\n"
|
|
"\tscale=%d\n"
|
|
"\troom=%d\n"
|
|
"\tdigits=[\n",
|
|
num->scale,
|
|
num->room);
|
|
for (p = num->buf; p < num->wp; ++p)
|
|
printf("\t\t%02d\n", *p);
|
|
printf("\t]\n");
|
|
}
|
|
|
|
/*
|
|
* error is not called from the implementation of the
|
|
* arithmetic functions because that can drive to memory
|
|
* leaks very easily.
|
|
*/
|
|
static void
|
|
error(char *fmt, ...)
|
|
{
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
xvprintf(fmt, va);
|
|
putc('\n', stderr);
|
|
va_end(va);
|
|
|
|
longjmp(env, 1);
|
|
}
|
|
|
|
static void
|
|
freenum(Num *num)
|
|
{
|
|
if (!num)
|
|
return;
|
|
free(num->buf);
|
|
free(num);
|
|
}
|
|
|
|
static Num *
|
|
moreroom(Num *num, int more)
|
|
{
|
|
int ro, wo, room;
|
|
signed char *p;
|
|
|
|
ro = num->rp - num->buf;
|
|
wo = num->wp - num->buf;
|
|
room = num->room;
|
|
|
|
if (room > INT_MAX - more)
|
|
eprintf("out of memory\n");
|
|
|
|
room = room + more;
|
|
if (room < NDIGITS)
|
|
room = NDIGITS;
|
|
p = erealloc(num->buf, room);
|
|
num->buf = p;
|
|
num->rp = p + ro;
|
|
num->wp = p + wo;
|
|
num->room = room;
|
|
|
|
return num;
|
|
}
|
|
|
|
static Num *
|
|
grow(Num *num)
|
|
{
|
|
return moreroom(num, NDIGITS);
|
|
}
|
|
|
|
static Num *
|
|
expand(Num *num, int min)
|
|
{
|
|
if (min < num->room)
|
|
return num;
|
|
return moreroom(num, min - num->room);
|
|
}
|
|
|
|
static Num *
|
|
new(int room)
|
|
{
|
|
Num *num = emalloc(sizeof(*num));
|
|
|
|
num->rp = num->wp = num->buf = NULL;
|
|
num->begin = num->room = num->scale = 0;
|
|
|
|
return moreroom(num, room);
|
|
}
|
|
|
|
static Num *
|
|
zeronum(int ndigits)
|
|
{
|
|
Num *num = new(ndigits);
|
|
|
|
num->wp = num->buf + ndigits;
|
|
memset(num->buf, 0, ndigits);
|
|
|
|
return num;
|
|
}
|
|
|
|
static Num *
|
|
wrdigit(Num *num, int d)
|
|
{
|
|
if (num->wp == &num->buf[num->room])
|
|
grow(num);
|
|
*num->wp++ = d;
|
|
|
|
return num;
|
|
}
|
|
|
|
static int
|
|
rddigit(Num *num)
|
|
{
|
|
if (num->rp == num->wp)
|
|
return -1;
|
|
return *num->rp++;
|
|
}
|
|
|
|
static int
|
|
peek(Num *num)
|
|
{
|
|
if (num->rp == num->wp)
|
|
return -1;
|
|
return *num->rp;
|
|
}
|
|
|
|
static Num *
|
|
poke(Num *num, unsigned d)
|
|
{
|
|
if (num->rp == &num->buf[num->room])
|
|
grow(num);
|
|
if (num->rp == num->wp)
|
|
num->wp++;
|
|
*num->rp = d;
|
|
|
|
return num;
|
|
}
|
|
|
|
static int
|
|
begin(Num *num)
|
|
{
|
|
return num->begin != 0;
|
|
}
|
|
|
|
static int
|
|
first(Num *num)
|
|
{
|
|
num->begin = 0;
|
|
num->rp = num->buf;
|
|
return num->rp != num->wp;
|
|
}
|
|
|
|
static int
|
|
last(Num *num)
|
|
{
|
|
if (num->wp != num->buf) {
|
|
num->begin = 0;
|
|
num->rp = num->wp - 1;
|
|
return 1;
|
|
}
|
|
num->begin = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
prev(Num *num)
|
|
{
|
|
if (num->rp > num->buf) {
|
|
--num->rp;
|
|
return 1;
|
|
}
|
|
num->begin = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
next(Num *num)
|
|
{
|
|
if (num->rp != num->wp + 1) {
|
|
++num->rp;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
numtrunc(Num *num)
|
|
{
|
|
num->wp = num->rp;
|
|
if (num->rp != num->buf)
|
|
num->rp--;
|
|
}
|
|
|
|
static int
|
|
more(Num *num)
|
|
{
|
|
return (num->rp != num->wp);
|
|
}
|
|
|
|
static int
|
|
length(Num *num)
|
|
{
|
|
return num->wp - num->buf;
|
|
}
|
|
|
|
static int
|
|
tell(Num *num)
|
|
{
|
|
return num->rp - num->buf;
|
|
}
|
|
|
|
static void
|
|
seek(Num *num, int pos)
|
|
{
|
|
num->rp = num->buf + pos;
|
|
}
|
|
|
|
static void
|
|
rshift(Num *num, int n)
|
|
{
|
|
int diff;
|
|
|
|
diff = length(num) - n;
|
|
if (diff < 0) {
|
|
first(num);
|
|
numtrunc(num);
|
|
return;
|
|
}
|
|
|
|
memmove(num->buf, num->buf + n, diff);
|
|
num->rp = num->buf + diff;
|
|
numtrunc(num);
|
|
}
|
|
|
|
static void
|
|
lshift(Num *num, int n)
|
|
{
|
|
int len;
|
|
|
|
len = length(num);
|
|
expand(num, len + n);
|
|
memmove(num->buf + n, num->buf, len);
|
|
memset(num->buf, 0, n);
|
|
num->wp += n;
|
|
}
|
|
|
|
static Num *
|
|
chsign(Num *num)
|
|
{
|
|
int val, d, carry;
|
|
|
|
carry = 0;
|
|
for (first(num); more(num); next(num)) {
|
|
d = peek(num);
|
|
val = 100 - d - carry;
|
|
|
|
carry = 1;
|
|
if (val >= 100) {
|
|
val -= 100;
|
|
carry = 0;
|
|
}
|
|
poke(num, val);
|
|
}
|
|
|
|
prev(num);
|
|
if (carry != 0) {
|
|
if (peek(num) == 99)
|
|
poke(num, -1);
|
|
else
|
|
wrdigit(num, -1);
|
|
} else {
|
|
if (peek(num) == 0)
|
|
numtrunc(num);
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
static Num *
|
|
copy(Num *num)
|
|
{
|
|
Num *p;
|
|
int len = length(num);
|
|
|
|
p = new(len);
|
|
memcpy(p->buf, num->buf, len);
|
|
p->wp = p->buf + len;
|
|
p->rp = p->buf;
|
|
p->scale = num->scale;
|
|
|
|
return p;
|
|
}
|
|
|
|
static int
|
|
negative(Num *num)
|
|
{
|
|
return last(num) && peek(num) == -1;
|
|
}
|
|
|
|
static Num *
|
|
norm(Num *n)
|
|
{
|
|
/* trailing 0 */
|
|
for (last(n); peek(n) == 0; numtrunc(n))
|
|
;
|
|
|
|
if (negative(n)) {
|
|
for (prev(n); peek(n) == 99; prev(n)) {
|
|
poke(n, -1);
|
|
next(n);
|
|
numtrunc(n);
|
|
}
|
|
}
|
|
|
|
/* adjust scale for 0 case*/
|
|
if (length(n) == 0)
|
|
n->scale = 0;
|
|
return n;
|
|
}
|
|
|
|
static Num *
|
|
mulnto(Num *src, Num *dst, int n)
|
|
{
|
|
div_t dd;
|
|
int d, carry;
|
|
|
|
first(dst);
|
|
numtrunc(dst);
|
|
|
|
carry = 0;
|
|
for (first(src); more(src); next(src)) {
|
|
d = peek(src) * n + carry;
|
|
dd = div(d, 100);
|
|
carry = dd.quot;
|
|
wrdigit(dst, dd.rem);
|
|
}
|
|
|
|
if (carry)
|
|
wrdigit(dst, carry);
|
|
return dst;
|
|
}
|
|
|
|
static Num *
|
|
muln(Num *num, int n)
|
|
{
|
|
div_t dd;
|
|
int d, carry;
|
|
|
|
carry = 0;
|
|
for (first(num); more(num); next(num)) {
|
|
d = peek(num) * n + carry;
|
|
dd = div(d, 100);
|
|
carry = dd.quot;
|
|
poke(num, dd.rem);
|
|
}
|
|
|
|
if (carry)
|
|
wrdigit(num, carry);
|
|
return num;
|
|
}
|
|
|
|
static int
|
|
divn(Num *num, int n)
|
|
{
|
|
div_t dd;
|
|
int val, carry;
|
|
|
|
carry = 0;
|
|
for (last(num); !begin(num); prev(num)) {
|
|
val = carry * 100 + peek(num);
|
|
dd = div(val, n);
|
|
poke(num, dd.quot);
|
|
carry = dd.rem;
|
|
}
|
|
norm(num);
|
|
|
|
return carry;
|
|
}
|
|
|
|
static void
|
|
div10(Num *num, int n)
|
|
{
|
|
div_t dd = div(n, 2);
|
|
|
|
if (dd.rem == 1)
|
|
divn(num, 10);
|
|
|
|
rshift(num, dd.quot);
|
|
}
|
|
|
|
static void
|
|
mul10(Num *num, int n)
|
|
{
|
|
div_t dd = div(n, 2);
|
|
|
|
if (dd.rem == 1)
|
|
muln(num, 10);
|
|
|
|
lshift(num, dd.quot);
|
|
}
|
|
|
|
static void
|
|
align(Num *a, Num *b)
|
|
{
|
|
int d;
|
|
Num *max, *min;
|
|
|
|
d = a->scale - b->scale;
|
|
if (d == 0) {
|
|
return;
|
|
} else if (d > 0) {
|
|
min = b;
|
|
max = a;
|
|
} else {
|
|
min = a;
|
|
max = b;
|
|
}
|
|
|
|
d = abs(d);
|
|
mul10(min, d);
|
|
min->scale += d;
|
|
|
|
assert(min->scale == max->scale);
|
|
}
|
|
|
|
static Num *
|
|
addn(Num *num, int val)
|
|
{
|
|
int d, carry = val;
|
|
|
|
for (first(num); carry; next(num)) {
|
|
d = more(num) ? peek(num) : 0;
|
|
d += carry;
|
|
carry = 0;
|
|
|
|
if (d >= 100) {
|
|
carry = 1;
|
|
d -= 100;
|
|
}
|
|
poke(num, d);
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
static Num *
|
|
reverse(Num *num)
|
|
{
|
|
int d;
|
|
signed char *p, *q;
|
|
|
|
for (p = num->buf, q = num->wp - 1; p < q; ++p, --q) {
|
|
d = *p;
|
|
*p = *q;
|
|
*q = d;
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
static Num *
|
|
addnum(Num *a, Num *b)
|
|
{
|
|
Num *c;
|
|
int len, alen, blen, carry, da, db, sum;
|
|
|
|
align(a, b);
|
|
alen = length(a);
|
|
blen = length(b);
|
|
len = (alen > blen) ? alen : blen;
|
|
c = new(len);
|
|
|
|
first(a);
|
|
first(b);
|
|
carry = 0;
|
|
while (len-- > 0) {
|
|
da = (more(a)) ? rddigit(a) : 0;
|
|
db = (more(b)) ? rddigit(b) : 0;
|
|
|
|
sum = da + db + carry;
|
|
if (sum >= 100) {
|
|
carry = 1;
|
|
sum -= 100;
|
|
} else if (sum < 0) {
|
|
carry = -1;
|
|
sum += 100;
|
|
} else {
|
|
carry = 0;
|
|
}
|
|
|
|
wrdigit(c, sum);
|
|
}
|
|
|
|
if (carry)
|
|
wrdigit(c, carry);
|
|
c->scale = a->scale;
|
|
|
|
return norm(c);
|
|
}
|
|
|
|
static Num *
|
|
subnum(Num *a, Num *b)
|
|
{
|
|
Num *tmp, *sum;
|
|
|
|
tmp = chsign(copy(b));
|
|
sum = addnum(a, tmp);
|
|
freenum(tmp);
|
|
|
|
return sum;
|
|
}
|
|
|
|
static Num *
|
|
mulnum(Num *a, Num *b)
|
|
{
|
|
Num shadow, *c, *ca, *cb;
|
|
int pos, prod, carry, dc, db, da, sc;
|
|
int asign = negative(a), bsign = negative(b);
|
|
|
|
c = zeronum(length(a) + length(b) + 1);
|
|
c->scale = a->scale + b->scale;
|
|
sc = (a->scale > b->scale) ? a->scale : b->scale;
|
|
|
|
ca = a;
|
|
if (asign)
|
|
ca = chsign(copy(ca));
|
|
cb = b;
|
|
if (bsign)
|
|
cb = chsign(copy(cb));
|
|
|
|
/* avoid aliasing problems when called from expnum*/
|
|
if (ca == cb) {
|
|
shadow = *cb;
|
|
b = cb = &shadow;
|
|
}
|
|
|
|
for (first(cb); more(cb); next(cb)) {
|
|
div_t d;
|
|
|
|
carry = 0;
|
|
db = peek(cb);
|
|
|
|
pos = tell(c);
|
|
for (first(ca); more(ca); next(ca)) {
|
|
da = peek(ca);
|
|
dc = peek(c);
|
|
prod = da * db + dc + carry;
|
|
d = div(prod, 100);
|
|
carry = d.quot;
|
|
poke(c, d.rem);
|
|
next(c);
|
|
}
|
|
|
|
for ( ; carry > 0; carry = d.quot) {
|
|
dc = peek(c) + carry;
|
|
d = div(dc, 100);
|
|
poke(c, d.rem);
|
|
next(c);
|
|
}
|
|
seek(c, pos + 1);
|
|
}
|
|
norm(c);
|
|
|
|
/* Adjust scale to max(a->scale, b->scale) while c is still positive */
|
|
sc = c->scale - sc;
|
|
if (sc > 0) {
|
|
div10(c, sc);
|
|
c->scale -= sc;
|
|
}
|
|
|
|
if (ca != a)
|
|
freenum(ca);
|
|
if (cb != b)
|
|
freenum(cb);
|
|
|
|
if (asign ^ bsign)
|
|
chsign(c);
|
|
return c;
|
|
}
|
|
|
|
/*
|
|
* The divmod function is implemented following the algorithm
|
|
* from the plan9 version that is not exactly like the one described
|
|
* in the paper. A lot of magic here.
|
|
*/
|
|
static Num *
|
|
divmod(Num *odivd, Num *odivr, Num **remp)
|
|
{
|
|
Num *acc, *divd, *divr, *res;
|
|
int divsign, remsign;
|
|
int under, magic, ndig, diff;
|
|
int d, q, carry, divcarry;
|
|
long dr, dd, cc;
|
|
|
|
divr = odivr;
|
|
acc = copy(&zero);
|
|
divd = copy(odivd);
|
|
res = zeronum(length(odivd));
|
|
|
|
under = divcarry = divsign = remsign = 0;
|
|
|
|
if (length(divr) == 0) {
|
|
weprintf("divide by 0\n");
|
|
goto ret;
|
|
}
|
|
|
|
divsign = negative(divd);
|
|
if (divsign)
|
|
chsign(divd);
|
|
|
|
remsign = negative(divr);
|
|
if (remsign)
|
|
divr = chsign(copy(divr));
|
|
|
|
diff = length(divd) - length(divr);
|
|
|
|
seek(res, diff + 1);
|
|
last(divd);
|
|
last(divr);
|
|
|
|
wrdigit(divd, 0);
|
|
|
|
dr = peek(divr);
|
|
magic = dr < 10;
|
|
dr = dr * 100 + (prev(divr) ? peek(divr) : 0);
|
|
if (magic) {
|
|
dr = dr * 100 + (prev(divr) ? peek(divr) : 0);
|
|
dr *= 2;
|
|
dr /= 25;
|
|
}
|
|
|
|
for (ndig = 0; diff >= 0; ++ndig) {
|
|
last(divd);
|
|
dd = peek(divd);
|
|
dd = dd * 100 + (prev(divd) ? peek(divd) : 0);
|
|
dd = dd * 100 + (prev(divd) ? peek(divd) : 0);
|
|
cc = dr;
|
|
|
|
if (diff == 0)
|
|
dd++;
|
|
else
|
|
cc++;
|
|
|
|
if (magic)
|
|
dd *= 8;
|
|
|
|
q = dd / cc;
|
|
under = 0;
|
|
if (q > 0 && dd % cc < 8 && magic) {
|
|
q--;
|
|
under = 1;
|
|
}
|
|
|
|
mulnto(divr, acc, q);
|
|
|
|
/* Subtract acc from dividend at offset position */
|
|
first(acc);
|
|
carry = 0;
|
|
for (seek(divd, diff); more(divd); next(divd)) {
|
|
d = peek(divd);
|
|
d = d - (more(acc) ? rddigit(acc) : 0) - carry;
|
|
carry = 0;
|
|
if (d < 0) {
|
|
d += 100;
|
|
carry = 1;
|
|
}
|
|
poke(divd, d);
|
|
}
|
|
divcarry = carry;
|
|
|
|
/* Store quotient digit */
|
|
prev(res);
|
|
poke(res, q);
|
|
|
|
/* Handle borrow propagation */
|
|
last(divd);
|
|
d = peek(divd);
|
|
if ((d != 0) && (diff != 0)) {
|
|
prev(divd);
|
|
d = peek(divd) + 100;
|
|
poke(divd, d);
|
|
}
|
|
|
|
/* Shorten dividend for next iteration */
|
|
if (--diff >= 0)
|
|
divd->wp--;
|
|
}
|
|
|
|
/*
|
|
* if we have an underflow then we have to adjust
|
|
* the remaining and the result
|
|
*/
|
|
if (under) {
|
|
Num *p = subnum(divd, divr);
|
|
if (negative(p)) {
|
|
freenum(p);
|
|
} else {
|
|
freenum(divd);
|
|
poke(res, q + 1);
|
|
divd = p;
|
|
}
|
|
}
|
|
|
|
if (divcarry) {
|
|
Num *p;
|
|
|
|
poke(res, q - 1);
|
|
poke(divd, -1);
|
|
p = addnum(divr, divd);
|
|
freenum(divd);
|
|
divd = p;
|
|
}
|
|
|
|
divcarry = 0;
|
|
for (first(res); more(res); next(res)) {
|
|
d = peek(res) + divcarry;
|
|
divcarry = 0;
|
|
if (d >= 100) {
|
|
d -= 100;
|
|
divcarry = 1;
|
|
}
|
|
poke(res, d);
|
|
}
|
|
|
|
ret:
|
|
if (divsign)
|
|
chsign(divd);
|
|
if (divsign ^ remsign)
|
|
chsign(res);
|
|
|
|
if (remp) {
|
|
divd->scale = odivd->scale;
|
|
*remp = norm(divd);
|
|
} else {
|
|
freenum(divd);
|
|
}
|
|
|
|
if (divr != odivr)
|
|
freenum(divr);
|
|
|
|
freenum(acc);
|
|
|
|
res->scale = odivd->scale - odivr->scale;
|
|
if (res->scale < 0)
|
|
res->scale = 0;
|
|
|
|
return norm(res);
|
|
}
|
|
|
|
static int
|
|
divscale(Num *divd, Num *divr)
|
|
{
|
|
int diff;
|
|
|
|
if (length(divr) == 0) {
|
|
weprintf("divide by 0\n");
|
|
return 0;
|
|
}
|
|
|
|
diff = scale + divr->scale - divd->scale;
|
|
|
|
if (diff > 0) {
|
|
mul10(divd, diff);
|
|
divd->scale += diff;
|
|
} else if (diff < 0) {
|
|
mul10(divr, -diff);
|
|
divr->scale += -diff;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static Num *
|
|
divnum(Num *a, Num *b)
|
|
{
|
|
if (!divscale(a, b))
|
|
return copy(&zero);
|
|
|
|
return divmod(a, b, NULL);
|
|
}
|
|
|
|
static Num *
|
|
modnum(Num *a, Num *b)
|
|
{
|
|
Num *mod, *c;
|
|
|
|
if (!divscale(a, b))
|
|
return copy(&zero);
|
|
|
|
c = divmod(a, b, &mod);
|
|
freenum(c);
|
|
|
|
return mod;
|
|
}
|
|
|
|
static Num *
|
|
expnum(Num *base, Num *exp)
|
|
{
|
|
int neg, d;
|
|
Num *res, *fact, *e, *tmp1, *tmp2;
|
|
|
|
res = copy(&one);
|
|
if (length(exp) == 0)
|
|
return res;
|
|
|
|
e = copy(exp);
|
|
if ((neg = negative(exp)) != 0)
|
|
chsign(e);
|
|
|
|
if (e->scale > 0) {
|
|
div10(e, e->scale);
|
|
e->scale = 0;
|
|
}
|
|
|
|
fact = copy(base);
|
|
while (length(e) > 0) {
|
|
first(e);
|
|
d = peek(e);
|
|
if (d % 2 == 1) {
|
|
tmp1 = mulnum(res, fact);
|
|
freenum(res);
|
|
res = tmp1;
|
|
}
|
|
|
|
/* Square fact */
|
|
tmp1 = mulnum(fact, fact);
|
|
freenum(fact);
|
|
fact = tmp1;
|
|
|
|
divn(e, 2);
|
|
}
|
|
freenum(fact);
|
|
freenum(e);
|
|
|
|
/* Handle negative exponent: 1 / res */
|
|
if (neg) {
|
|
tmp2 = divnum(tmp1 = copy(&one), res);
|
|
freenum(tmp1);
|
|
freenum(res);
|
|
res = tmp2;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Compare two numbers: returns <0 if a<b, 0 if a==b, >0 if a>b
|
|
*/
|
|
static int
|
|
cmpnum(Num *a, Num *b)
|
|
{
|
|
Num *diff;
|
|
int result;
|
|
|
|
diff = subnum(a, b);
|
|
if (length(diff) == 0)
|
|
result = 0;
|
|
else if (negative(diff))
|
|
result = -1;
|
|
else
|
|
result = 1;
|
|
freenum(diff);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Integer square root of a small integer (0-9999)
|
|
* Used for initial guess in Newton's method
|
|
*/
|
|
static int
|
|
isqrt(int n)
|
|
{
|
|
int x, x1;
|
|
|
|
if (n <= 0)
|
|
return 0;
|
|
if (n == 1)
|
|
return 1;
|
|
|
|
x = n;
|
|
x1 = (x + 1) / 2;
|
|
while (x1 < x) {
|
|
x = x1;
|
|
x1 = (x + n / x) / 2;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
/*
|
|
* Square root using Newton's method: x_{n+1} = (x_n + y/x_n) / 2
|
|
*
|
|
* Key insight: sqrt(a * 10^(2n)) = sqrt(a) * 10^n
|
|
* So we scale up the input to get the desired output precision.
|
|
*
|
|
* To compute sqrt with scale decimal places of precision:
|
|
* 1. Scale up y by 10^(2*scale + 2) (extra 2 for guard digits)
|
|
* 2. Compute integer sqrt
|
|
* 3. Result has (scale + 1) decimal places, numtrunc to scale
|
|
*/
|
|
static Num *
|
|
sqrtnum(Num *oy)
|
|
{
|
|
Num *y, *x, *xprev, *q, *sum;
|
|
int top, ysc, iter;
|
|
|
|
if (length(oy) == 0)
|
|
return copy(&zero);
|
|
|
|
if (negative(oy)) {
|
|
weprintf("square root of negative number\n");
|
|
return copy(&zero);
|
|
}
|
|
|
|
y = copy(oy);
|
|
ysc = 2 * scale + 2 - y->scale;
|
|
if (ysc > 0)
|
|
mul10(y, ysc);
|
|
ysc = 2 * scale + 2;
|
|
|
|
/* Make scale even (so sqrt gives integer result) */
|
|
if (ysc % 2 == 1) {
|
|
muln(y, 10);
|
|
ysc++;
|
|
}
|
|
y->scale = 0;
|
|
|
|
last(y);
|
|
top = peek(y);
|
|
if (prev(y) && length(y) > 1)
|
|
top = top * 100 + peek(y);
|
|
|
|
x = new(0);
|
|
wrdigit(x, isqrt(top));
|
|
x->scale = 0;
|
|
|
|
/* Scale up the initial guess to match the magnitude of y */
|
|
lshift(x, (length(y) - 1) / 2);
|
|
|
|
|
|
/* Newton iteration: x = (x + y/x) / 2 */
|
|
xprev = NULL;
|
|
for (iter = 0; iter < 1000; iter++) {
|
|
q = divmod(y, x, NULL);
|
|
sum = addnum(x, q);
|
|
freenum(q);
|
|
divn(sum, 2);
|
|
|
|
/* Check for convergence: sum == x or sum == prev */
|
|
if (cmpnum(sum, x) == 0) {
|
|
freenum(sum);
|
|
break;
|
|
}
|
|
if (xprev != NULL && cmpnum(sum, xprev) == 0) {
|
|
/* Oscillating - pick smaller */
|
|
if (cmpnum(x, sum) < 0) {
|
|
freenum(sum);
|
|
} else {
|
|
freenum(x);
|
|
x = sum;
|
|
}
|
|
break;
|
|
}
|
|
|
|
freenum(xprev);
|
|
xprev = x;
|
|
x = sum;
|
|
}
|
|
freenum(xprev);
|
|
freenum(y);
|
|
|
|
/* Truncate to desired scale */
|
|
x->scale = ysc / 2;
|
|
if (x->scale > scale) {
|
|
int diff = x->scale - scale;
|
|
div10(x, diff);
|
|
x->scale = scale;
|
|
}
|
|
|
|
return norm(x);
|
|
}
|
|
|
|
static Num *
|
|
tonum(void)
|
|
{
|
|
char *s, *t, *end, *dot;
|
|
Num *num, *denom, *numer, *frac, *q, *rem;
|
|
int sign, d, ch, nfrac;
|
|
|
|
s = input->s;
|
|
num = new(0);
|
|
sign = 0;
|
|
if (*s == '_') {
|
|
sign = 1;
|
|
++s;
|
|
}
|
|
|
|
dot = NULL;
|
|
for (t = s; (ch = *t) > 0 || ch <= UCHAR_MAX; ++t) {
|
|
if (!strchr(digits, ch))
|
|
break;
|
|
if (ch == '.') {
|
|
if (dot)
|
|
break;
|
|
dot = t;
|
|
}
|
|
}
|
|
input->s = end = t;
|
|
|
|
/*
|
|
* Parse integer part: process digits left-to-right.
|
|
* For each digit: num = num * ibase + digit
|
|
*/
|
|
for (t = s; t < (dot ? dot : end); ++t) {
|
|
d = strchr(digits, *t) - digits;
|
|
muln(num, ibase);
|
|
addn(num, d);
|
|
}
|
|
norm(num);
|
|
|
|
if (!dot)
|
|
goto ret;
|
|
|
|
/*
|
|
* convert fractional digits
|
|
* Algorithm: For digits d[0], d[1], ..., d[n-1] after '.'
|
|
* Value = d[0]/ibase + d[1]/ibase^2 + ... + d[n-1]/ibase^n
|
|
*
|
|
* numerator = d[0]*ibase^(n-1) + d[1]*ibase^(n-2) + ... + d[n-1]
|
|
* denominator = ibase^n
|
|
* Then extract decimal digits by repeated: num*100/denom
|
|
*/
|
|
denom = copy(&one);
|
|
numer = copy(&zero);
|
|
for (t = dot + 1; t < end; ++t) {
|
|
d = strchr(digits, *t) - digits;
|
|
muln(denom, ibase);
|
|
muln(numer, ibase);
|
|
addn(numer, d);
|
|
}
|
|
|
|
nfrac = end - dot - 1;
|
|
frac = new(0);
|
|
d = 0;
|
|
while (frac->scale < nfrac || length(numer) > 0) {
|
|
muln(numer, 100);
|
|
q = divmod(numer, denom, &rem);
|
|
freenum(numer);
|
|
|
|
d = first(q) ? peek(q) : 0;
|
|
wrdigit(frac, d);
|
|
freenum(q);
|
|
numer = rem;
|
|
frac->scale += 2;
|
|
}
|
|
reverse(frac);
|
|
|
|
/* Trim to exact input scale for odd nfrac */
|
|
if (frac->scale > nfrac && d % 10 == 0) {
|
|
divn(frac, 10);
|
|
frac->scale--;
|
|
}
|
|
|
|
freenum(numer);
|
|
freenum(denom);
|
|
|
|
q = addnum(num, frac);
|
|
freenum(num);
|
|
freenum(frac);
|
|
num = q;
|
|
|
|
ret:
|
|
if (sign)
|
|
chsign(num);
|
|
return num;
|
|
}
|
|
|
|
static void
|
|
prchr(int ch)
|
|
{
|
|
if (col >= 69) {
|
|
putchar('\\');
|
|
putchar('\n');
|
|
col = 0;
|
|
}
|
|
putchar(ch);
|
|
col++;
|
|
}
|
|
|
|
static void
|
|
printd(int d, int base, int space)
|
|
{
|
|
int w, n;
|
|
|
|
if (base <= 16) {
|
|
prchr(digits[d]);
|
|
} else {
|
|
if (space)
|
|
prchr(' ');
|
|
|
|
for (w = 1, n = base - 1; n >= 10; n /= 10)
|
|
w++;
|
|
|
|
if (col + w > 69) {
|
|
putchar('\\');
|
|
putchar('\n');
|
|
col = 0;
|
|
}
|
|
col += printf("%0*d", w, d);
|
|
}
|
|
}
|
|
|
|
static void
|
|
pushdigit(struct digit **l, int val)
|
|
{
|
|
struct digit *it = emalloc(sizeof(*it));
|
|
|
|
it->next = *l;
|
|
it->val = val;
|
|
*l = it;
|
|
}
|
|
|
|
static int
|
|
popdigit(struct digit **l)
|
|
{
|
|
int val;
|
|
struct digit *next, *it = *l;
|
|
|
|
if (it == NULL)
|
|
return -1;
|
|
|
|
val = it->val;
|
|
next = it->next;
|
|
free(it);
|
|
*l = next;
|
|
return val;
|
|
}
|
|
|
|
static void
|
|
printnum(Num *onum, int base)
|
|
{
|
|
struct digit *sp;
|
|
int sc, i, sign, n;
|
|
Num *num, *inte, *frac, *opow;
|
|
|
|
col = 0;
|
|
if (length(onum) == 0) {
|
|
prchr('0');
|
|
return;
|
|
}
|
|
|
|
num = copy(onum);
|
|
if ((sign = negative(num)) != 0)
|
|
chsign(num);
|
|
|
|
sc = num->scale;
|
|
if (num->scale % 2 == 1) {
|
|
muln(num, 10);
|
|
num->scale++;
|
|
}
|
|
inte = copy(num);
|
|
rshift(inte, num->scale / 2);
|
|
inte->scale = 0;
|
|
frac = subnum(num, inte);
|
|
|
|
sp = NULL;
|
|
for (i = 0; length(inte) > 0; ++i)
|
|
pushdigit(&sp, divn(inte, base));
|
|
if (sign)
|
|
prchr('-');
|
|
while (i-- > 0)
|
|
printd(popdigit(&sp), base, 1);
|
|
assert(sp == NULL);
|
|
|
|
if (num->scale == 0)
|
|
goto ret;
|
|
|
|
/*
|
|
* Print fractional part by repeated multiplication by base.
|
|
* We maintain the fraction as: frac / 10^scale
|
|
*
|
|
* Algorithm:
|
|
* 1. Multiply frac by base
|
|
* 2. Output integer part (frac / 10^scale)
|
|
* 3. Keep fractional part (frac % 10^scale)
|
|
*/
|
|
prchr('.');
|
|
|
|
opow = copy(&one);
|
|
mul10(opow, num->scale);
|
|
|
|
for (n = 0; n < sc; ++n) {
|
|
int d;
|
|
Num *q, *rem;
|
|
|
|
muln(frac, base);
|
|
q = divmod(frac, opow, &rem);
|
|
d = first(q) ? peek(q) : 0;
|
|
freenum(frac);
|
|
freenum(q);
|
|
frac = rem;
|
|
printd(d, base, n > 0);
|
|
}
|
|
freenum(opow);
|
|
|
|
ret:
|
|
freenum(num);
|
|
freenum(inte);
|
|
freenum(frac);
|
|
}
|
|
|
|
static int
|
|
moreinput(void)
|
|
{
|
|
struct input *ip;
|
|
|
|
repeat:
|
|
if (!input)
|
|
return 0;
|
|
|
|
if (input->buf != NULL && *input->s != '\0')
|
|
return 1;
|
|
|
|
if (input->fp) {
|
|
if (getline(&input->buf, &input->n, input->fp) >= 0) {
|
|
input->s = input->buf;
|
|
return 1;
|
|
}
|
|
if (ferror(input->fp)) {
|
|
eprintf("reading from file:");
|
|
exit(1);
|
|
}
|
|
fclose(input->fp);
|
|
}
|
|
|
|
ip = input;
|
|
input = ip->next;
|
|
free(ip->buf);
|
|
free(ip);
|
|
goto repeat;
|
|
}
|
|
|
|
static void
|
|
addinput(FILE *fp, char *s)
|
|
{
|
|
struct input *ip;
|
|
|
|
assert((!fp && !s) == 0);
|
|
|
|
ip = emalloc(sizeof(*ip));
|
|
ip->next = input;
|
|
ip->fp = fp;
|
|
ip->n = 0;
|
|
ip->s = ip->buf = s;
|
|
input = ip;
|
|
}
|
|
|
|
static void
|
|
delinput(int cmd, int n)
|
|
{
|
|
if (n < 0)
|
|
error("Q command requires a number >= 0");
|
|
while (n-- > 0) {
|
|
if (cmd == 'Q' && !input->next)
|
|
error("Q command argument exceeded string execution depth");
|
|
if (input->fp)
|
|
fclose(input->fp);
|
|
free(input->buf);
|
|
input = input->next;
|
|
if (!input)
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
push(Val v)
|
|
{
|
|
Val *p = emalloc(sizeof(Val));
|
|
|
|
*p = v;
|
|
p->next = stack;
|
|
stack = p;
|
|
}
|
|
|
|
static void
|
|
needstack(int n)
|
|
{
|
|
Val *vp;
|
|
|
|
for (vp = stack; n > 0 && vp; vp = vp->next)
|
|
--n;
|
|
if (n > 0)
|
|
error("stack empty");
|
|
}
|
|
|
|
static Val
|
|
pop(void)
|
|
{
|
|
Val v;
|
|
|
|
if (!stack)
|
|
error("stack empty");
|
|
v = *stack;
|
|
free(stack);
|
|
stack = v.next;
|
|
|
|
return v;
|
|
}
|
|
|
|
static Num *
|
|
popnum(void)
|
|
{
|
|
Val v = pop();
|
|
|
|
if (v.type != NUM) {
|
|
free(v.u.s);
|
|
error("non-numeric value");
|
|
}
|
|
return v.u.n;
|
|
}
|
|
|
|
static void
|
|
pushnum(Num *num)
|
|
{
|
|
push((Val) {.type = NUM, .u.n = num});
|
|
}
|
|
|
|
static void
|
|
pushstr(char *s)
|
|
{
|
|
push((Val) {.type = STR, .u.s = s});
|
|
}
|
|
|
|
static void
|
|
arith(Num *(*fn)(Num *, Num *))
|
|
{
|
|
Num *a, *b, *c;
|
|
|
|
needstack(2);
|
|
b = popnum();
|
|
a = popnum();
|
|
c = (*fn)(a, b);
|
|
freenum(a);
|
|
freenum(b);
|
|
pushnum(c);
|
|
}
|
|
|
|
static void
|
|
pushdivmod(void)
|
|
{
|
|
Num *a, *b, *q, *rem;
|
|
|
|
needstack(2);
|
|
b = popnum();
|
|
a = popnum();
|
|
|
|
if (!divscale(a, b)) {
|
|
q = copy(&zero);
|
|
rem = copy(&zero);
|
|
} else {
|
|
q = divmod(a, b, &rem);
|
|
}
|
|
|
|
pushnum(q);
|
|
pushnum(rem);
|
|
freenum(a);
|
|
freenum(b);
|
|
}
|
|
|
|
static int
|
|
popint(void)
|
|
{
|
|
Num *num;
|
|
int r = -1, n, d;
|
|
|
|
num = popnum();
|
|
if (negative(num))
|
|
goto ret;
|
|
|
|
/* discard fraction part */
|
|
div10(num, num->scale);
|
|
|
|
n = 0;
|
|
for (last(num); !begin(num); prev(num)) {
|
|
if (n > INT_MAX / 100)
|
|
goto ret;
|
|
n *= 100;
|
|
d = peek(num);
|
|
if (n > INT_MAX - d)
|
|
goto ret;
|
|
n += d;
|
|
}
|
|
r = n;
|
|
|
|
ret:
|
|
freenum(num);
|
|
return r;
|
|
}
|
|
|
|
static void
|
|
pushint(int n)
|
|
{
|
|
div_t dd;
|
|
Num *num;
|
|
|
|
num = new(0);
|
|
for ( ; n > 0; n = dd.quot) {
|
|
dd = div(n, 100);
|
|
wrdigit(num, dd.rem);
|
|
}
|
|
pushnum(num);
|
|
}
|
|
|
|
static void
|
|
printval(Val v)
|
|
{
|
|
if (v.type == STR)
|
|
fputs(v.u.s, stdout);
|
|
else
|
|
printnum(v.u.n, obase);
|
|
}
|
|
|
|
static Val
|
|
dupval(Val v)
|
|
{
|
|
Val nv;
|
|
|
|
switch (nv.type = v.type) {
|
|
case STR:
|
|
nv.u.s = estrdup(v.u.s);
|
|
break;
|
|
case NUM:
|
|
nv.u.n = copy(v.u.n);
|
|
break;
|
|
case NVAL:
|
|
nv.type = NUM;
|
|
nv.u.n = copy(&zero);
|
|
break;
|
|
}
|
|
|
|
return nv;
|
|
}
|
|
|
|
static void
|
|
freeval(Val v)
|
|
{
|
|
if (v.type == STR)
|
|
free(v.u.s);
|
|
else if (v.type == NUM)
|
|
freenum(v.u.n);
|
|
}
|
|
|
|
static void
|
|
dumpstack(void)
|
|
{
|
|
Val *vp;
|
|
|
|
for (vp = stack; vp; vp = vp->next) {
|
|
printval(*vp);
|
|
putchar('\n');
|
|
}
|
|
}
|
|
|
|
static void
|
|
clearstack(void)
|
|
{
|
|
Val *vp, *next;
|
|
|
|
for (vp = stack; vp; vp = next) {
|
|
next = vp->next;
|
|
freeval(*vp);
|
|
free(vp);
|
|
}
|
|
stack = NULL;
|
|
}
|
|
|
|
static void
|
|
dupstack(void)
|
|
{
|
|
Val v;
|
|
|
|
push(v = pop());
|
|
push(dupval(v));
|
|
}
|
|
|
|
static void
|
|
deepstack(void)
|
|
{
|
|
int n;
|
|
Val *vp;
|
|
|
|
n = 0;
|
|
for (vp = stack; vp; vp = vp->next) {
|
|
if (n == INT_MAX)
|
|
error("stack depth does not fit in a integer");
|
|
++n;
|
|
}
|
|
pushint(n);
|
|
}
|
|
|
|
static void
|
|
pushfrac(void)
|
|
{
|
|
Val v = pop();
|
|
|
|
if (v.type == STR)
|
|
pushint(0);
|
|
else
|
|
pushint(v.u.n->scale);
|
|
freeval(v);
|
|
}
|
|
|
|
static void
|
|
pushlen(void)
|
|
{
|
|
int n;
|
|
Num *num;
|
|
Val v = pop();
|
|
|
|
if (v.type == STR) {
|
|
n = strlen(v.u.s);
|
|
} else {
|
|
num = v.u.n;
|
|
if (length(num) == 0) {
|
|
n = 1;
|
|
} else {
|
|
n = length(num) * 2;
|
|
n -= last(num) ? peek(num) < 10 : 0;
|
|
}
|
|
}
|
|
pushint(n);
|
|
freeval(v);
|
|
}
|
|
|
|
static void
|
|
setibase(void)
|
|
{
|
|
int n = popint();
|
|
|
|
if (n < 2 || n > 16)
|
|
error("input base must be an integer between 2 and 16");
|
|
ibase = n;
|
|
}
|
|
|
|
static void
|
|
setobase(void)
|
|
{
|
|
int n = popint();
|
|
|
|
if (n < 2)
|
|
error("output base must be an integer greater than 1");
|
|
obase = n;
|
|
}
|
|
|
|
static char *
|
|
string(char *dst, int *np)
|
|
{
|
|
int n, ch;
|
|
|
|
n = np ? *np : 0;
|
|
for (;;) {
|
|
ch = *input->s++;
|
|
|
|
switch (ch) {
|
|
case '\0':
|
|
if (!moreinput())
|
|
exit(0);
|
|
break;
|
|
case '\\':
|
|
if (*input->s == '[') {
|
|
dst = erealloc(dst, n + 1);
|
|
dst[n++] = *input->s++;
|
|
break;
|
|
}
|
|
goto copy;
|
|
case ']':
|
|
if (!np) {
|
|
dst = erealloc(dst, n + 1);
|
|
dst[n] = '\0';
|
|
return dst;
|
|
}
|
|
case '[':
|
|
default:
|
|
copy:
|
|
dst = erealloc(dst, n + 1);
|
|
dst[n++] = ch;
|
|
if (ch == '[')
|
|
dst = string(dst, &n);
|
|
if (ch == ']') {
|
|
*np = n;
|
|
return dst;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
setscale(void)
|
|
{
|
|
int n = popint();
|
|
|
|
if (n < 0)
|
|
error("scale must be a nonnegative integer");
|
|
scale = n;
|
|
}
|
|
|
|
static unsigned
|
|
hash(char *name)
|
|
{
|
|
int c;
|
|
unsigned h = 5381;
|
|
|
|
while (c = *name++)
|
|
h = h*33 ^ c;
|
|
|
|
return h;
|
|
}
|
|
|
|
static struct reg *
|
|
lookup(char *name)
|
|
{
|
|
struct reg *rp;
|
|
int h = hash(name) & HASHSIZ-1;
|
|
|
|
for (rp = htbl[h]; rp; rp = rp->next) {
|
|
if (strcmp(name, rp->name) == 0)
|
|
return rp;
|
|
}
|
|
|
|
rp = emalloc(sizeof(*rp));
|
|
rp->next = htbl[h];
|
|
htbl[h] = rp;
|
|
rp->name = estrdup(name);
|
|
|
|
rp->val.type = NVAL;
|
|
rp->val.next = NULL;
|
|
|
|
rp->ary.n = 0;
|
|
rp->ary.buf = NULL;
|
|
rp->ary.next = NULL;
|
|
|
|
return rp;
|
|
}
|
|
|
|
static char *
|
|
regname(void)
|
|
{
|
|
int delim, ch;
|
|
char *s;
|
|
static char name[REGSIZ];
|
|
|
|
ch = *input->s++;
|
|
if (!iflag || ch != '<' && ch != '"') {
|
|
name[0] = ch;
|
|
name[1] = '\0';
|
|
return name;
|
|
}
|
|
|
|
if ((delim = ch) == '<')
|
|
delim = '>';
|
|
|
|
for (s = name; s < &name[REGSIZ]; ++s) {
|
|
ch = *input->s++;
|
|
if (ch == '\0' || ch == delim) {
|
|
*s = '\0';
|
|
if (ch == '>') {
|
|
name[0] = atoi(name);
|
|
name[1] = '\0';
|
|
}
|
|
return name;
|
|
}
|
|
*s = ch;
|
|
}
|
|
|
|
error("identifier too long");
|
|
}
|
|
|
|
static void
|
|
popreg(void)
|
|
{
|
|
int i;
|
|
Val *vnext;
|
|
struct ary *anext;
|
|
char *s = regname();
|
|
struct reg *rp = lookup(s);
|
|
|
|
if (rp->val.type == NVAL)
|
|
error("stack register '%s' (%o) is empty", s, s[0]);
|
|
|
|
push(rp->val);
|
|
vnext = rp->val.next;
|
|
if (!vnext) {
|
|
rp->val.type = NVAL;
|
|
} else {
|
|
rp->val = *vnext;
|
|
free(vnext);
|
|
}
|
|
|
|
for (i = 0; i < rp->ary.n; ++i)
|
|
freeval(rp->ary.buf[i]);
|
|
free(rp->ary.buf);
|
|
|
|
anext = rp->ary.next;
|
|
if (!anext) {
|
|
rp->ary.n = 0;
|
|
rp->ary.buf = NULL;
|
|
} else {
|
|
rp->ary = *anext;
|
|
free(anext);
|
|
}
|
|
}
|
|
|
|
static void
|
|
pushreg(void)
|
|
{
|
|
Val v;
|
|
Val *vp;
|
|
struct ary *ap;
|
|
struct reg *rp = lookup(regname());
|
|
|
|
v = pop();
|
|
|
|
vp = emalloc(sizeof(Val));
|
|
*vp = rp->val;
|
|
rp->val = v;
|
|
rp->val.next = vp;
|
|
|
|
ap = emalloc(sizeof(struct ary));
|
|
*ap = rp->ary;
|
|
rp->ary.n = 0;
|
|
rp->ary.buf = NULL;
|
|
rp->ary.next = ap;
|
|
}
|
|
|
|
static Val *
|
|
aryidx(void)
|
|
{
|
|
int n;
|
|
int i;
|
|
Val *vp;
|
|
struct reg *rp = lookup(regname());
|
|
struct ary *ap = &rp->ary;
|
|
|
|
n = popint();
|
|
if (n < 0)
|
|
error("array index must fit in a positive integer");
|
|
|
|
if (n >= ap->n) {
|
|
ap->buf = ereallocarray(ap->buf, n+1, sizeof(Val));
|
|
for (i = ap->n; i <= n; ++i)
|
|
ap->buf[i].type = NVAL;
|
|
ap->n = n + 1;
|
|
}
|
|
return &ap->buf[n];
|
|
}
|
|
|
|
static void
|
|
aryget(void)
|
|
{
|
|
Val *vp = aryidx();
|
|
|
|
push(dupval(*vp));
|
|
}
|
|
|
|
static void
|
|
aryset(void)
|
|
{
|
|
Val val, *vp = aryidx();
|
|
|
|
val = pop();
|
|
freeval(*vp);
|
|
*vp = val;
|
|
}
|
|
|
|
static void
|
|
execmacro(void)
|
|
{
|
|
int ch;
|
|
Val v = pop();
|
|
|
|
assert(v.type != NVAL);
|
|
if (v.type == NUM) {
|
|
push(v);
|
|
return;
|
|
}
|
|
|
|
if (input->fp) {
|
|
addinput(NULL, v.u.s);
|
|
return;
|
|
}
|
|
|
|
/* check for tail recursion */
|
|
for (ch = *input->s; ch > 0 && ch < UCHAR_MAX; ch = *input->s) {
|
|
if (!isspace(ch))
|
|
break;
|
|
++input->s;
|
|
}
|
|
|
|
if (ch == '\0') {
|
|
free(input->buf);
|
|
input->buf = input->s = v.u.s;
|
|
return;
|
|
}
|
|
|
|
addinput(NULL, v.u.s);
|
|
}
|
|
|
|
static void
|
|
relational(int ch)
|
|
{
|
|
int r;
|
|
char *s;
|
|
Num *a, *b;
|
|
struct reg *rp = lookup(regname());
|
|
|
|
needstack(2);
|
|
a = popnum();
|
|
b = popnum();
|
|
r = cmpnum(a, b);
|
|
freenum(a);
|
|
freenum(b);
|
|
|
|
switch (ch) {
|
|
case '>':
|
|
r = r > 0;
|
|
break;
|
|
case '<':
|
|
r = r < 0;
|
|
break;
|
|
case '=':
|
|
r = r == 0;
|
|
break;
|
|
case LE:
|
|
r = r <= 0;
|
|
break;
|
|
case GE:
|
|
r = r >= 0;
|
|
break;
|
|
case NE:
|
|
r = r != 0;
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
if (!r)
|
|
return;
|
|
|
|
push(dupval(rp->val));
|
|
execmacro();
|
|
}
|
|
|
|
static void
|
|
printbytes(void)
|
|
{
|
|
Num *num;
|
|
Val v = pop();
|
|
|
|
if (v.type == STR) {
|
|
fputs(v.u.s, stdout);
|
|
} else {
|
|
num = v.u.n;
|
|
div10(num, num->scale);
|
|
num->scale = 0;
|
|
printnum(num, 100);
|
|
}
|
|
freeval(v);
|
|
}
|
|
|
|
static void
|
|
eval(void)
|
|
{
|
|
int ch;
|
|
char *s;
|
|
Num *num;
|
|
size_t siz;
|
|
Val v1, v2;
|
|
struct reg *rp;
|
|
|
|
if (setjmp(env))
|
|
return;
|
|
|
|
for (s = input->s; (ch = *s) != '\0'; ++s) {
|
|
if (ch < 0 || ch > UCHAR_MAX || !isspace(ch))
|
|
break;
|
|
}
|
|
input->s = s + (ch != '\0');
|
|
|
|
switch (ch) {
|
|
case '\0':
|
|
break;
|
|
case 'n':
|
|
v1 = pop();
|
|
printval(v1);
|
|
freeval(v1);
|
|
break;
|
|
case 'p':
|
|
v1 = pop();
|
|
printval(v1);
|
|
putchar('\n');
|
|
push(v1);
|
|
break;
|
|
case 'P':
|
|
printbytes();
|
|
break;
|
|
case 'f':
|
|
dumpstack();
|
|
break;
|
|
case '+':
|
|
arith(addnum);
|
|
break;
|
|
case '-':
|
|
arith(subnum);
|
|
break;
|
|
case '*':
|
|
arith(mulnum);
|
|
break;
|
|
case '/':
|
|
arith(divnum);
|
|
break;
|
|
case '%':
|
|
arith(modnum);
|
|
break;
|
|
case '^':
|
|
arith(expnum);
|
|
break;
|
|
case '~':
|
|
pushdivmod();
|
|
break;
|
|
case 'v':
|
|
num = popnum();
|
|
pushnum(sqrtnum(num));
|
|
freenum(num);
|
|
break;
|
|
case 'c':
|
|
clearstack();
|
|
break;
|
|
case 'd':
|
|
dupstack();
|
|
break;
|
|
case 'r':
|
|
needstack(2);
|
|
v1 = pop();
|
|
v2 = pop();
|
|
push(v1);
|
|
push(v2);
|
|
break;
|
|
case 'S':
|
|
pushreg();
|
|
break;
|
|
case 'L':
|
|
popreg();
|
|
break;
|
|
case 's':
|
|
rp = lookup(regname());
|
|
freeval(rp->val);
|
|
rp->val = pop();
|
|
break;
|
|
case 'l':
|
|
rp = lookup(regname());
|
|
push(dupval(rp->val));
|
|
break;
|
|
case 'i':
|
|
setibase();
|
|
break;
|
|
case 'o':
|
|
setobase();
|
|
break;
|
|
case 'k':
|
|
setscale();
|
|
break;
|
|
case 'I':
|
|
pushint(ibase);
|
|
break;
|
|
case 'O':
|
|
pushint(obase);
|
|
break;
|
|
case 'K':
|
|
pushint(scale);
|
|
break;
|
|
case '[':
|
|
pushstr(string(NULL, NULL));
|
|
break;
|
|
case 'x':
|
|
execmacro();
|
|
break;
|
|
case '!':
|
|
switch (*input->s++) {
|
|
case '<':
|
|
ch = GE;
|
|
break;
|
|
case '>':
|
|
ch = LE;
|
|
break;
|
|
case '=':
|
|
ch = NE;
|
|
break;
|
|
default:
|
|
system(input->s-1);
|
|
goto discard;
|
|
}
|
|
case '>':
|
|
case '<':
|
|
case '=':
|
|
relational(ch);
|
|
break;
|
|
case '?':
|
|
s = NULL;
|
|
if (getline(&s, &siz, stdin) > 0) {
|
|
pushstr(s);
|
|
} else {
|
|
free(s);
|
|
if (ferror(stdin))
|
|
eprintf("reading from file\n");
|
|
}
|
|
break;
|
|
case 'q':
|
|
delinput('q', 2);
|
|
break;
|
|
case 'Q':
|
|
delinput('Q', popint());
|
|
break;
|
|
case 'Z':
|
|
pushlen();
|
|
break;
|
|
case 'X':
|
|
pushfrac();
|
|
break;
|
|
case 'z':
|
|
deepstack();
|
|
break;
|
|
case '#':
|
|
discard:
|
|
while (*input->s)
|
|
++input->s;
|
|
break;
|
|
case ':':
|
|
aryset();
|
|
break;
|
|
case ';':
|
|
aryget();
|
|
break;
|
|
default:
|
|
if (!strchr(digits, ch))
|
|
error("'%c' (%#o) unimplemented", ch, ch);
|
|
case '_':
|
|
--input->s;
|
|
pushnum(tonum());
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dc(char *fname)
|
|
{
|
|
FILE *fp;
|
|
|
|
if (strcmp(fname, "-") == 0) {
|
|
fp = stdin;
|
|
} else {
|
|
if ((fp = fopen(fname, "r")) == NULL)
|
|
eprintf("opening '%s':", fname);
|
|
}
|
|
addinput(fp, NULL);
|
|
|
|
while (moreinput())
|
|
eval();
|
|
|
|
fclose(fp);
|
|
free(input);
|
|
input = NULL;
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
eprintf("usage: dc [-i] [file ...]\n");
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
ARGBEGIN {
|
|
case 'i':
|
|
iflag = 1;
|
|
break;
|
|
default:
|
|
usage();
|
|
} ARGEND
|
|
|
|
while (*argv)
|
|
dc(*argv++);
|
|
dc("-");
|
|
|
|
return 0;
|
|
}
|