#include "config.h"
#include <stdio.h>
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_MEMORY_H
# include <memory.h>
# else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif
#endif
#include "dc.h"
#include "dc-proto.h"
typedef enum {DC_FALSE, DC_TRUE} dc_boolean;
typedef enum {
DC_OKAY = DC_SUCCESS,
DC_EATONE,
DC_QUIT,
DC_INT,
DC_STR,
DC_SYSTEM,
DC_COMMENT,
DC_NEGCMP,
DC_EOF_ERROR
} dc_status;
static int dc_ibase=10;
static int dc_obase=10;
static int dc_scale=0;
static int unwind_depth=0;
static dc_boolean unwind_noexit=DC_FALSE;
static int stdin_lookahead=EOF;
static FILE *input_fil_fp;
static const char *input_str_string;
static int input_pushback;
static int
input_fil DC_DECLVOID()
{
if (input_pushback != EOF){
int c = input_pushback;
input_pushback = EOF;
return c;
}
return getc(input_fil_fp);
}
static int
input_str DC_DECLVOID()
{
if (!*input_str_string)
return EOF;
return *input_str_string++;
}
static int
dc_eval_and_free_str DC_DECLARG((string))
dc_data string DC_DECLEND
{
dc_status status;
status = dc_evalstr(string);
if (string.dc_type == DC_STRING)
dc_free_str(&string.v.string);
return status;
}
static dc_status
dc_func DC_DECLARG((c, peekc, negcmp))
int c DC_DECLSEP
int peekc DC_DECLSEP
int negcmp DC_DECLEND
{
dc_data datum;
int tmpint;
switch (c){
case '_': case '.':
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
case '8': case '9': case 'A': case 'B':
case 'C': case 'D': case 'E': case 'F':
return DC_INT;
case ' ':
case '\t':
case '\n':
break;
case '+':
dc_binop(dc_add, dc_scale);
break;
case '-':
dc_binop(dc_sub, dc_scale);
break;
case '*':
dc_binop(dc_mul, dc_scale);
break;
case '/':
dc_binop(dc_div, dc_scale);
break;
case '%':
dc_binop(dc_rem, dc_scale);
break;
case '~':
dc_binop2(dc_divrem, dc_scale);
break;
case '|':
dc_triop(dc_modexp, dc_scale);
break;
case '^':
dc_binop(dc_exp, dc_scale);
break;
case '<':
if (peekc == EOF)
return DC_EOF_ERROR;
if ( (dc_cmpop() < 0) == !negcmp )
if (dc_register_get(peekc, &datum) == DC_SUCCESS)
if (dc_eval_and_free_str(datum) == DC_QUIT)
return DC_QUIT;
return DC_EATONE;
case '=':
if (peekc == EOF)
return DC_EOF_ERROR;
if ( (dc_cmpop() == 0) == !negcmp )
if (dc_register_get(peekc, &datum) == DC_SUCCESS)
if (dc_eval_and_free_str(datum) == DC_QUIT)
return DC_QUIT;
return DC_EATONE;
case '>':
if (peekc == EOF)
return DC_EOF_ERROR;
if ( (dc_cmpop() > 0) == !negcmp )
if (dc_register_get(peekc, &datum) == DC_SUCCESS)
if (dc_eval_and_free_str(datum) == DC_QUIT)
return DC_QUIT;
return DC_EATONE;
case '?':
if (stdin_lookahead != EOF){
ungetc(stdin_lookahead, stdin);
stdin_lookahead = EOF;
}
if (dc_eval_and_free_str(dc_readstring(stdin, '\n', '\n')) == DC_QUIT)
return DC_QUIT;
return DC_OKAY;
case '[':
return DC_STR;
case '!':
if (peekc == '<' || peekc == '=' || peekc == '>')
return DC_NEGCMP;
return DC_SYSTEM;
case '#':
return DC_COMMENT;
case 'a':
if (dc_pop(&datum) == DC_SUCCESS){
char tmps;
if (datum.dc_type == DC_NUMBER){
tmps = (char) dc_num2int(datum.v.number, DC_TOSS);
}else if (datum.dc_type == DC_STRING){
tmps = *dc_str2charp(datum.v.string);
dc_free_str(&datum.v.string);
}else{
dc_garbage("at top of stack", -1);
}
dc_push(dc_makestring(&tmps, 1));
}
break;
case 'c':
dc_clear_stack();
break;
case 'd':
if (dc_top_of_stack(&datum) == DC_SUCCESS)
dc_push(dc_dup(datum));
break;
case 'f':
dc_printall(dc_obase);
break;
case 'i':
if (dc_pop(&datum) == DC_SUCCESS){
tmpint = 0;
if (datum.dc_type == DC_NUMBER)
tmpint = dc_num2int(datum.v.number, DC_TOSS);
if ( ! (2 <= tmpint && tmpint <= DC_IBASE_MAX) )
fprintf(stderr,
"%s: input base must be a number \
between 2 and %d (inclusive)\n",
progname, DC_IBASE_MAX);
else
dc_ibase = tmpint;
}
break;
case 'k':
if (dc_pop(&datum) == DC_SUCCESS){
tmpint = -1;
if (datum.dc_type == DC_NUMBER)
tmpint = dc_num2int(datum.v.number, DC_TOSS);
if ( ! (tmpint >= 0) )
fprintf(stderr,
"%s: scale must be a nonnegative number\n",
progname);
else
dc_scale = tmpint;
}
break;
case 'l':
if (peekc == EOF)
return DC_EOF_ERROR;
if (dc_register_get(peekc, &datum) == DC_SUCCESS)
dc_push(datum);
return DC_EATONE;
case 'n':
if (dc_pop(&datum) == DC_SUCCESS)
dc_print(datum, dc_obase, DC_NONL, DC_TOSS);
break;
case 'o':
if (dc_pop(&datum) == DC_SUCCESS){
tmpint = 0;
if (datum.dc_type == DC_NUMBER)
tmpint = dc_num2int(datum.v.number, DC_TOSS);
if ( ! (tmpint > 1) )
fprintf(stderr,
"%s: output base must be a number greater than 1\n",
progname);
else
dc_obase = tmpint;
}
break;
case 'p':
if (dc_top_of_stack(&datum) == DC_SUCCESS)
dc_print(datum, dc_obase, DC_WITHNL, DC_KEEP);
break;
case 'q':
unwind_depth = 1;
unwind_noexit = DC_FALSE;
return DC_QUIT;
case 'r':
if (dc_pop(&datum) == DC_SUCCESS) {
dc_data datum2;
int two_status;
two_status = dc_pop(&datum2);
dc_push(datum);
if (two_status == DC_SUCCESS)
dc_push(datum2);
}
break;
case 's':
if (peekc == EOF)
return DC_EOF_ERROR;
if (dc_pop(&datum) == DC_SUCCESS)
dc_register_set(peekc, datum);
return DC_EATONE;
case 'v':
if (dc_pop(&datum) == DC_SUCCESS){
dc_num tmpnum;
if (datum.dc_type != DC_NUMBER){
fprintf(stderr,
"%s: square root of nonnumeric attempted\n",
progname);
}else if (dc_sqrt(datum.v.number, dc_scale, &tmpnum) == DC_SUCCESS){
dc_free_num(&datum.v.number);
datum.v.number = tmpnum;
dc_push(datum);
}
}
break;
case 'x':
if (dc_pop(&datum) == DC_SUCCESS){
if (datum.dc_type == DC_STRING){
if (dc_eval_and_free_str(datum) == DC_QUIT)
return DC_QUIT;
}else if (datum.dc_type == DC_NUMBER){
dc_push(datum);
}else{
dc_garbage("at top of stack", -1);
}
}
break;
case 'z':
dc_push(dc_int2data(dc_tell_stackdepth()));
break;
case 'I':
dc_push(dc_int2data(dc_ibase));
break;
case 'K':
dc_push(dc_int2data(dc_scale));
break;
case 'L':
if (peekc == EOF)
return DC_EOF_ERROR;
if (dc_register_pop(peekc, &datum) == DC_SUCCESS)
dc_push(datum);
return DC_EATONE;
case 'O':
dc_push(dc_int2data(dc_obase));
break;
case 'P':
if (dc_pop(&datum) == DC_SUCCESS){
if (datum.dc_type == DC_NUMBER)
dc_dump_num(datum.v.number, DC_TOSS);
else if (datum.dc_type == DC_STRING)
dc_out_str(datum.v.string, DC_NONL, DC_TOSS);
else
dc_garbage("at top of stack", -1);
}
break;
case 'Q':
if (dc_pop(&datum) == DC_SUCCESS){
unwind_depth = 0;
unwind_noexit = DC_TRUE;
if (datum.dc_type == DC_NUMBER)
unwind_depth = dc_num2int(datum.v.number, DC_TOSS);
if (unwind_depth-- > 0)
return DC_QUIT;
unwind_depth = 0;
fprintf(stderr,
"%s: Q command requires a number >= 1\n",
progname);
}
break;
#if 0
case 'R':
if (dc_pop(&datum) == DC_SUCCESS){
tmpint = 0;
if (datum.dc_type == DC_NUMBER)
tmpint = dc_num2int(datum.v.number, DC_TOSS);
dc_stack_rotate(tmpint);
}
break;
#endif
case 'S':
if (peekc == EOF)
return DC_EOF_ERROR;
if (dc_pop(&datum) == DC_SUCCESS)
dc_register_push(peekc, datum);
return DC_EATONE;
case 'X':
if (dc_pop(&datum) == DC_SUCCESS){
tmpint = 0;
if (datum.dc_type == DC_NUMBER)
tmpint = dc_tell_scale(datum.v.number, DC_TOSS);
dc_push(dc_int2data(tmpint));
}
break;
case 'Z':
if (dc_pop(&datum) == DC_SUCCESS)
dc_push(dc_int2data(dc_tell_length(datum, DC_TOSS)));
break;
case ':':
if (peekc == EOF)
return DC_EOF_ERROR;
if (dc_pop(&datum) == DC_SUCCESS){
tmpint = -1;
if (datum.dc_type == DC_NUMBER)
tmpint = dc_num2int(datum.v.number, DC_TOSS);
if (dc_pop(&datum) == DC_SUCCESS){
if (tmpint < 0)
fprintf(stderr,
"%s: array index must be a nonnegative integer\n",
progname);
else
dc_array_set(peekc, tmpint, datum);
}
}
return DC_EATONE;
case ';':
if (peekc == EOF)
return DC_EOF_ERROR;
if (dc_pop(&datum) == DC_SUCCESS){
tmpint = -1;
if (datum.dc_type == DC_NUMBER)
tmpint = dc_num2int(datum.v.number, DC_TOSS);
if (tmpint < 0)
fprintf(stderr,
"%s: array index must be a nonnegative integer\n",
progname);
else
dc_push(dc_array_get(peekc, tmpint));
}
return DC_EATONE;
default:
fprintf(stderr, "%s: ", progname);
dc_show_id(stdout, c, " unimplemented\n");
break;
}
return DC_OKAY;
}
int
dc_evalstr DC_DECLARG((string))
dc_data string DC_DECLEND
{
const char *s;
const char *end;
const char *p;
size_t len;
int c;
int peekc;
int count;
int negcmp;
int next_negcmp = 0;
if (string.dc_type != DC_STRING){
fprintf(stderr,
"%s: eval called with non-string argument\n",
progname);
return DC_OKAY;
}
s = dc_str2charp(string.v.string);
end = s + dc_strlen(string.v.string);
while (s < end){
c = *(const unsigned char *)s++;
peekc = EOF;
if (s < end)
peekc = *(const unsigned char *)s;
negcmp = next_negcmp;
next_negcmp = 0;
switch (dc_func(c, peekc, negcmp)){
case DC_OKAY:
break;
case DC_EATONE:
if (peekc != EOF)
++s;
break;
case DC_QUIT:
if (unwind_depth > 0){
--unwind_depth;
return DC_QUIT;
}
return DC_OKAY;
case DC_INT:
input_str_string = s - 1;
dc_push(dc_getnum(input_str, dc_ibase, &peekc));
s = input_str_string;
if (peekc != EOF)
--s;
break;
case DC_STR:
count = 1;
for (p=s; p<end && count>0; ++p)
if (*p == ']')
--count;
else if (*p == '[')
++count;
len = p - s;
dc_push(dc_makestring(s, len-1));
s = p;
break;
case DC_SYSTEM:
s = dc_system(s);
case DC_COMMENT:
s = memchr(s, '\n', (size_t)(end-s));
if (!s)
s = end;
else
++s;
break;
case DC_NEGCMP:
next_negcmp = 1;
break;
case DC_EOF_ERROR:
fprintf(stderr, "%s: unexpected EOS\n", progname);
return DC_OKAY;
}
}
return DC_OKAY;
}
int
dc_evalfile DC_DECLARG((fp))
FILE *fp DC_DECLEND
{
int c;
int peekc;
int negcmp;
int next_negcmp = 0;
dc_data datum;
stdin_lookahead = EOF;
for (c=getc(fp); c!=EOF; c=peekc){
peekc = getc(fp);
if (fp == stdin)
stdin_lookahead = peekc;
negcmp = next_negcmp;
next_negcmp = 0;
switch (dc_func(c, peekc, negcmp)){
case DC_OKAY:
if (stdin_lookahead != peekc && fp == stdin)
peekc = getc(fp);
break;
case DC_EATONE:
peekc = getc(fp);
break;
case DC_QUIT:
if (unwind_noexit != DC_TRUE)
return DC_SUCCESS;
fprintf(stderr,
"%s: Q command argument exceeded string execution depth\n",
progname);
if (stdin_lookahead != peekc && fp == stdin)
peekc = getc(fp);
break;
case DC_INT:
input_fil_fp = fp;
input_pushback = c;
ungetc(peekc, fp);
dc_push(dc_getnum(input_fil, dc_ibase, &peekc));
break;
case DC_STR:
ungetc(peekc, fp);
datum = dc_readstring(fp, '[', ']');
dc_push(datum);
peekc = getc(fp);
break;
case DC_SYSTEM:
ungetc(peekc, fp);
datum = dc_readstring(stdin, '\n', '\n');
(void)dc_system(dc_str2charp(datum.v.string));
dc_free_str(&datum.v.string);
peekc = getc(fp);
break;
case DC_COMMENT:
while (peekc!=EOF && peekc!='\n')
peekc = getc(fp);
if (peekc != EOF)
peekc = getc(fp);
break;
case DC_NEGCMP:
next_negcmp = 1;
break;
case DC_EOF_ERROR:
fprintf(stderr, "%s: unexpected EOF\n", progname);
return DC_FAIL;
}
}
return DC_SUCCESS;
}