Inputting fractional non-decimal numbers has never worked correctly in our OpenBSD-derived dc(1). It truncates the input to a number of decimal places equal to the number of hexadecimal (or whatever base) places given on the input. That's unacceptable, because many numbers require more precision to represent in base 10 than in their original bases. Fix this bug by using as many decimal places as needed to represent the input, up to the maximum of the global scale factor. This has one mildly surprising side effect: the scale of a number entered in non-decimal mode will no longer necessarily equal the number of hexadecimal (or whatever base) places given on the input. I think that's an acceptable behavior change, given that inputting fractional non-decimal numbers never worked in the first place, and the man page doesn't specify whether trailing zeros on the input should affect a number's scale. PR: 206230 Reported by: nibbana@gmx.us Reviewed by: pfg Differential Revision: https://reviews.freebsd.org/D13336
1757 lines
30 KiB
C
1757 lines
30 KiB
C
/* $OpenBSD: bcode.c,v 1.46 2014/10/08 03:59:56 doug Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2003, Otto Moerbeek <otto@drijf.net>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <err.h>
|
|
#include <limits.h>
|
|
#include <openssl/ssl.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "extern.h"
|
|
|
|
/* #define DEBUGGING */
|
|
|
|
#define MAX_ARRAY_INDEX 2048
|
|
#define READSTACK_SIZE 8
|
|
|
|
#define NO_ELSE -2 /* -1 is EOF */
|
|
#define REG_ARRAY_SIZE_SMALL (UCHAR_MAX + 1)
|
|
#define REG_ARRAY_SIZE_BIG (UCHAR_MAX + 1 + USHRT_MAX + 1)
|
|
|
|
struct bmachine {
|
|
struct source *readstack;
|
|
struct stack *reg;
|
|
struct stack stack;
|
|
u_int scale;
|
|
u_int obase;
|
|
u_int ibase;
|
|
size_t readsp;
|
|
size_t reg_array_size;
|
|
size_t readstack_sz;
|
|
bool extended_regs;
|
|
};
|
|
|
|
static struct bmachine bmachine;
|
|
|
|
static __inline int readch(void);
|
|
static __inline void unreadch(void);
|
|
static __inline char *readline(void);
|
|
static __inline void src_free(void);
|
|
|
|
static u_long get_ulong(struct number *);
|
|
|
|
static __inline void push_number(struct number *);
|
|
static __inline void push_string(char *);
|
|
static __inline void push(struct value *);
|
|
static __inline struct value *tos(void);
|
|
static __inline struct number *pop_number(void);
|
|
static __inline char *pop_string(void);
|
|
static __inline void clear_stack(void);
|
|
static __inline void print_tos(void);
|
|
static void print_err(void);
|
|
static void pop_print(void);
|
|
static void pop_printn(void);
|
|
static __inline void print_stack(void);
|
|
static __inline void dup(void);
|
|
static void swap(void);
|
|
static void drop(void);
|
|
|
|
static void get_scale(void);
|
|
static void set_scale(void);
|
|
static void get_obase(void);
|
|
static void set_obase(void);
|
|
static void get_ibase(void);
|
|
static void set_ibase(void);
|
|
static void stackdepth(void);
|
|
static void push_scale(void);
|
|
static u_int count_digits(const struct number *);
|
|
static void num_digits(void);
|
|
static void to_ascii(void);
|
|
static void push_line(void);
|
|
static void comment(void);
|
|
static void bexec(char *);
|
|
static void badd(void);
|
|
static void bsub(void);
|
|
static void bmul(void);
|
|
static void bdiv(void);
|
|
static void bmod(void);
|
|
static void bdivmod(void);
|
|
static void bexp(void);
|
|
static bool bsqrt_stop(const BIGNUM *, const BIGNUM *, u_int *);
|
|
static void bsqrt(void);
|
|
static void not(void);
|
|
static void equal_numbers(void);
|
|
static void less_numbers(void);
|
|
static void lesseq_numbers(void);
|
|
static void equal(void);
|
|
static void not_equal(void);
|
|
static void less(void);
|
|
static void not_less(void);
|
|
static void greater(void);
|
|
static void not_greater(void);
|
|
static void not_compare(void);
|
|
static bool compare_numbers(enum bcode_compare, struct number *,
|
|
struct number *);
|
|
static void compare(enum bcode_compare);
|
|
static int readreg(void);
|
|
static void load(void);
|
|
static void store(void);
|
|
static void load_stack(void);
|
|
static void store_stack(void);
|
|
static void load_array(void);
|
|
static void store_array(void);
|
|
static void nop(void);
|
|
static void quit(void);
|
|
static void quitN(void);
|
|
static void skipN(void);
|
|
static void skip_until_mark(void);
|
|
static void parse_number(void);
|
|
static void unknown(void);
|
|
static void eval_string(char *);
|
|
static void eval_line(void);
|
|
static void eval_tos(void);
|
|
|
|
|
|
typedef void (*opcode_function)(void);
|
|
|
|
struct jump_entry {
|
|
u_char ch;
|
|
opcode_function f;
|
|
};
|
|
|
|
static opcode_function jump_table[UCHAR_MAX];
|
|
|
|
static const struct jump_entry jump_table_data[] = {
|
|
{ ' ', nop },
|
|
{ '!', not_compare },
|
|
{ '#', comment },
|
|
{ '%', bmod },
|
|
{ '(', less_numbers },
|
|
{ '*', bmul },
|
|
{ '+', badd },
|
|
{ '-', bsub },
|
|
{ '.', parse_number },
|
|
{ '/', bdiv },
|
|
{ '0', parse_number },
|
|
{ '1', parse_number },
|
|
{ '2', parse_number },
|
|
{ '3', parse_number },
|
|
{ '4', parse_number },
|
|
{ '5', parse_number },
|
|
{ '6', parse_number },
|
|
{ '7', parse_number },
|
|
{ '8', parse_number },
|
|
{ '9', parse_number },
|
|
{ ':', store_array },
|
|
{ ';', load_array },
|
|
{ '<', less },
|
|
{ '=', equal },
|
|
{ '>', greater },
|
|
{ '?', eval_line },
|
|
{ 'A', parse_number },
|
|
{ 'B', parse_number },
|
|
{ 'C', parse_number },
|
|
{ 'D', parse_number },
|
|
{ 'E', parse_number },
|
|
{ 'F', parse_number },
|
|
{ 'G', equal_numbers },
|
|
{ 'I', get_ibase },
|
|
{ 'J', skipN },
|
|
{ 'K', get_scale },
|
|
{ 'L', load_stack },
|
|
{ 'M', nop },
|
|
{ 'N', not },
|
|
{ 'O', get_obase },
|
|
{ 'P', pop_print },
|
|
{ 'Q', quitN },
|
|
{ 'R', drop },
|
|
{ 'S', store_stack },
|
|
{ 'X', push_scale },
|
|
{ 'Z', num_digits },
|
|
{ '[', push_line },
|
|
{ '\f', nop },
|
|
{ '\n', nop },
|
|
{ '\r', nop },
|
|
{ '\t', nop },
|
|
{ '^', bexp },
|
|
{ '_', parse_number },
|
|
{ 'a', to_ascii },
|
|
{ 'c', clear_stack },
|
|
{ 'd', dup },
|
|
{ 'e', print_err },
|
|
{ 'f', print_stack },
|
|
{ 'i', set_ibase },
|
|
{ 'k', set_scale },
|
|
{ 'l', load },
|
|
{ 'n', pop_printn },
|
|
{ 'o', set_obase },
|
|
{ 'p', print_tos },
|
|
{ 'q', quit },
|
|
{ 'r', swap },
|
|
{ 's', store },
|
|
{ 'v', bsqrt },
|
|
{ 'x', eval_tos },
|
|
{ 'z', stackdepth },
|
|
{ '{', lesseq_numbers },
|
|
{ '~', bdivmod }
|
|
};
|
|
|
|
#define JUMP_TABLE_DATA_SIZE \
|
|
(sizeof(jump_table_data)/sizeof(jump_table_data[0]))
|
|
|
|
void
|
|
init_bmachine(bool extended_registers)
|
|
{
|
|
unsigned int i;
|
|
|
|
bmachine.extended_regs = extended_registers;
|
|
bmachine.reg_array_size = bmachine.extended_regs ?
|
|
REG_ARRAY_SIZE_BIG : REG_ARRAY_SIZE_SMALL;
|
|
|
|
bmachine.reg = calloc(bmachine.reg_array_size,
|
|
sizeof(bmachine.reg[0]));
|
|
if (bmachine.reg == NULL)
|
|
err(1, NULL);
|
|
|
|
for (i = 0; i < UCHAR_MAX; i++)
|
|
jump_table[i] = unknown;
|
|
for (i = 0; i < JUMP_TABLE_DATA_SIZE; i++)
|
|
jump_table[jump_table_data[i].ch] = jump_table_data[i].f;
|
|
|
|
stack_init(&bmachine.stack);
|
|
|
|
for (i = 0; i < bmachine.reg_array_size; i++)
|
|
stack_init(&bmachine.reg[i]);
|
|
|
|
bmachine.readstack_sz = READSTACK_SIZE;
|
|
bmachine.readstack = calloc(sizeof(struct source),
|
|
bmachine.readstack_sz);
|
|
if (bmachine.readstack == NULL)
|
|
err(1, NULL);
|
|
bmachine.obase = bmachine.ibase = 10;
|
|
}
|
|
|
|
u_int
|
|
bmachine_scale(void)
|
|
{
|
|
return bmachine.scale;
|
|
}
|
|
|
|
/* Reset the things needed before processing a (new) file */
|
|
void
|
|
reset_bmachine(struct source *src)
|
|
{
|
|
|
|
bmachine.readsp = 0;
|
|
bmachine.readstack[0] = *src;
|
|
}
|
|
|
|
static __inline int
|
|
readch(void)
|
|
{
|
|
struct source *src = &bmachine.readstack[bmachine.readsp];
|
|
|
|
return (src->vtable->readchar(src));
|
|
}
|
|
|
|
static __inline void
|
|
unreadch(void)
|
|
{
|
|
struct source *src = &bmachine.readstack[bmachine.readsp];
|
|
|
|
src->vtable->unreadchar(src);
|
|
}
|
|
|
|
static __inline char *
|
|
readline(void)
|
|
{
|
|
struct source *src = &bmachine.readstack[bmachine.readsp];
|
|
|
|
return (src->vtable->readline(src));
|
|
}
|
|
|
|
static __inline void
|
|
src_free(void)
|
|
{
|
|
struct source *src = &bmachine.readstack[bmachine.readsp];
|
|
|
|
src->vtable->free(src);
|
|
}
|
|
|
|
#ifdef DEBUGGING
|
|
void
|
|
pn(const char *str, const struct number *n)
|
|
{
|
|
char *p = BN_bn2dec(n->number);
|
|
|
|
if (p == NULL)
|
|
err(1, "BN_bn2dec failed");
|
|
fputs(str, stderr);
|
|
fprintf(stderr, " %s (%u)\n" , p, n->scale);
|
|
OPENSSL_free(p);
|
|
}
|
|
|
|
void
|
|
pbn(const char *str, const BIGNUM *n)
|
|
{
|
|
char *p = BN_bn2dec(n);
|
|
|
|
if (p == NULL)
|
|
err(1, "BN_bn2dec failed");
|
|
fputs(str, stderr);
|
|
fprintf(stderr, " %s\n", p);
|
|
OPENSSL_free(p);
|
|
}
|
|
|
|
#endif
|
|
|
|
static unsigned long factors[] = {
|
|
0, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
|
|
100000000, 1000000000
|
|
};
|
|
|
|
/* Multiply n by 10^s */
|
|
void
|
|
scale_number(BIGNUM *n, int s)
|
|
{
|
|
unsigned int abs_scale;
|
|
|
|
if (s == 0)
|
|
return;
|
|
|
|
abs_scale = s > 0 ? s : -s;
|
|
|
|
if (abs_scale < sizeof(factors)/sizeof(factors[0])) {
|
|
if (s > 0)
|
|
bn_check(BN_mul_word(n, factors[abs_scale]));
|
|
else
|
|
BN_div_word(n, factors[abs_scale]);
|
|
} else {
|
|
BIGNUM *a, *p;
|
|
BN_CTX *ctx;
|
|
|
|
a = BN_new();
|
|
bn_checkp(a);
|
|
p = BN_new();
|
|
bn_checkp(p);
|
|
ctx = BN_CTX_new();
|
|
bn_checkp(ctx);
|
|
|
|
bn_check(BN_set_word(a, 10));
|
|
bn_check(BN_set_word(p, abs_scale));
|
|
bn_check(BN_exp(a, a, p, ctx));
|
|
if (s > 0)
|
|
bn_check(BN_mul(n, n, a, ctx));
|
|
else
|
|
bn_check(BN_div(n, NULL, n, a, ctx));
|
|
BN_CTX_free(ctx);
|
|
BN_free(a);
|
|
BN_free(p);
|
|
}
|
|
}
|
|
|
|
void
|
|
split_number(const struct number *n, BIGNUM *i, BIGNUM *f)
|
|
{
|
|
u_long rem;
|
|
|
|
bn_checkp(BN_copy(i, n->number));
|
|
|
|
if (n->scale == 0 && f != NULL)
|
|
bn_check(BN_zero(f));
|
|
else if (n->scale < sizeof(factors)/sizeof(factors[0])) {
|
|
rem = BN_div_word(i, factors[n->scale]);
|
|
if (f != NULL)
|
|
bn_check(BN_set_word(f, rem));
|
|
} else {
|
|
BIGNUM *a, *p;
|
|
BN_CTX *ctx;
|
|
|
|
a = BN_new();
|
|
bn_checkp(a);
|
|
p = BN_new();
|
|
bn_checkp(p);
|
|
ctx = BN_CTX_new();
|
|
bn_checkp(ctx);
|
|
|
|
bn_check(BN_set_word(a, 10));
|
|
bn_check(BN_set_word(p, n->scale));
|
|
bn_check(BN_exp(a, a, p, ctx));
|
|
bn_check(BN_div(i, f, n->number, a, ctx));
|
|
BN_CTX_free(ctx);
|
|
BN_free(a);
|
|
BN_free(p);
|
|
}
|
|
}
|
|
|
|
/* Change the scale of n to s. Reducing scale may truncate the mantissa */
|
|
void
|
|
normalize(struct number *n, u_int s)
|
|
{
|
|
|
|
scale_number(n->number, s - n->scale);
|
|
n->scale = s;
|
|
}
|
|
|
|
static u_long
|
|
get_ulong(struct number *n)
|
|
{
|
|
|
|
normalize(n, 0);
|
|
return (BN_get_word(n->number));
|
|
}
|
|
|
|
void
|
|
negate(struct number *n)
|
|
{
|
|
BN_set_negative(n->number, !BN_is_negative(n->number));
|
|
}
|
|
|
|
static __inline void
|
|
push_number(struct number *n)
|
|
{
|
|
|
|
stack_pushnumber(&bmachine.stack, n);
|
|
}
|
|
|
|
static __inline void
|
|
push_string(char *string)
|
|
{
|
|
|
|
stack_pushstring(&bmachine.stack, string);
|
|
}
|
|
|
|
static __inline void
|
|
push(struct value *v)
|
|
{
|
|
|
|
stack_push(&bmachine.stack, v);
|
|
}
|
|
|
|
static __inline struct value *
|
|
tos(void)
|
|
{
|
|
|
|
return (stack_tos(&bmachine.stack));
|
|
}
|
|
|
|
static __inline struct value *
|
|
pop(void)
|
|
{
|
|
|
|
return (stack_pop(&bmachine.stack));
|
|
}
|
|
|
|
static __inline struct number *
|
|
pop_number(void)
|
|
{
|
|
|
|
return (stack_popnumber(&bmachine.stack));
|
|
}
|
|
|
|
static __inline char *
|
|
pop_string(void)
|
|
{
|
|
|
|
return (stack_popstring(&bmachine.stack));
|
|
}
|
|
|
|
static __inline void
|
|
clear_stack(void)
|
|
{
|
|
|
|
stack_clear(&bmachine.stack);
|
|
}
|
|
|
|
static __inline void
|
|
print_stack(void)
|
|
{
|
|
|
|
stack_print(stdout, &bmachine.stack, "", bmachine.obase);
|
|
}
|
|
|
|
static __inline void
|
|
print_tos(void)
|
|
{
|
|
struct value *value = tos();
|
|
|
|
if (value != NULL) {
|
|
print_value(stdout, value, "", bmachine.obase);
|
|
putchar('\n');
|
|
}
|
|
else
|
|
warnx("stack empty");
|
|
}
|
|
|
|
static void
|
|
print_err(void)
|
|
{
|
|
struct value *value = tos();
|
|
if (value != NULL) {
|
|
print_value(stderr, value, "", bmachine.obase);
|
|
(void)putc('\n', stderr);
|
|
}
|
|
else
|
|
warnx("stack empty");
|
|
}
|
|
|
|
static void
|
|
pop_print(void)
|
|
{
|
|
struct value *value = pop();
|
|
|
|
if (value != NULL) {
|
|
switch (value->type) {
|
|
case BCODE_NONE:
|
|
break;
|
|
case BCODE_NUMBER:
|
|
normalize(value->u.num, 0);
|
|
print_ascii(stdout, value->u.num);
|
|
fflush(stdout);
|
|
break;
|
|
case BCODE_STRING:
|
|
fputs(value->u.string, stdout);
|
|
fflush(stdout);
|
|
break;
|
|
}
|
|
stack_free_value(value);
|
|
}
|
|
}
|
|
|
|
static void
|
|
pop_printn(void)
|
|
{
|
|
struct value *value = pop();
|
|
|
|
if (value != NULL) {
|
|
print_value(stdout, value, "", bmachine.obase);
|
|
fflush(stdout);
|
|
stack_free_value(value);
|
|
}
|
|
}
|
|
|
|
static __inline void
|
|
dup(void)
|
|
{
|
|
|
|
stack_dup(&bmachine.stack);
|
|
}
|
|
|
|
static void
|
|
swap(void)
|
|
{
|
|
|
|
stack_swap(&bmachine.stack);
|
|
}
|
|
|
|
static void
|
|
drop(void)
|
|
{
|
|
struct value *v = pop();
|
|
if (v != NULL)
|
|
stack_free_value(v);
|
|
}
|
|
|
|
static void
|
|
get_scale(void)
|
|
{
|
|
struct number *n;
|
|
|
|
n = new_number();
|
|
bn_check(BN_set_word(n->number, bmachine.scale));
|
|
push_number(n);
|
|
}
|
|
|
|
static void
|
|
set_scale(void)
|
|
{
|
|
struct number *n;
|
|
u_long scale;
|
|
|
|
n = pop_number();
|
|
if (n != NULL) {
|
|
if (BN_is_negative(n->number))
|
|
warnx("scale must be a nonnegative number");
|
|
else {
|
|
scale = get_ulong(n);
|
|
if (scale != BN_MASK2 && scale <= UINT_MAX)
|
|
bmachine.scale = (u_int)scale;
|
|
else
|
|
warnx("scale too large");
|
|
}
|
|
free_number(n);
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_obase(void)
|
|
{
|
|
struct number *n;
|
|
|
|
n = new_number();
|
|
bn_check(BN_set_word(n->number, bmachine.obase));
|
|
push_number(n);
|
|
}
|
|
|
|
static void
|
|
set_obase(void)
|
|
{
|
|
struct number *n;
|
|
u_long base;
|
|
|
|
n = pop_number();
|
|
if (n != NULL) {
|
|
base = get_ulong(n);
|
|
if (base != BN_MASK2 && base > 1 && base <= UINT_MAX)
|
|
bmachine.obase = (u_int)base;
|
|
else
|
|
warnx("output base must be a number greater than 1");
|
|
free_number(n);
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_ibase(void)
|
|
{
|
|
struct number *n;
|
|
|
|
n = new_number();
|
|
bn_check(BN_set_word(n->number, bmachine.ibase));
|
|
push_number(n);
|
|
}
|
|
|
|
static void
|
|
set_ibase(void)
|
|
{
|
|
struct number *n;
|
|
u_long base;
|
|
|
|
n = pop_number();
|
|
if (n != NULL) {
|
|
base = get_ulong(n);
|
|
if (base != BN_MASK2 && 2 <= base && base <= 16)
|
|
bmachine.ibase = (u_int)base;
|
|
else
|
|
warnx("input base must be a number between 2 and 16 "
|
|
"(inclusive)");
|
|
free_number(n);
|
|
}
|
|
}
|
|
|
|
static void
|
|
stackdepth(void)
|
|
{
|
|
struct number *n;
|
|
size_t i;
|
|
|
|
i = stack_size(&bmachine.stack);
|
|
n = new_number();
|
|
bn_check(BN_set_word(n->number, i));
|
|
push_number(n);
|
|
}
|
|
|
|
static void
|
|
push_scale(void)
|
|
{
|
|
struct number *n;
|
|
struct value *value;
|
|
u_int scale = 0;
|
|
|
|
value = pop();
|
|
if (value != NULL) {
|
|
switch (value->type) {
|
|
case BCODE_NONE:
|
|
return;
|
|
case BCODE_NUMBER:
|
|
scale = value->u.num->scale;
|
|
break;
|
|
case BCODE_STRING:
|
|
break;
|
|
}
|
|
stack_free_value(value);
|
|
n = new_number();
|
|
bn_check(BN_set_word(n->number, scale));
|
|
push_number(n);
|
|
}
|
|
}
|
|
|
|
static u_int
|
|
count_digits(const struct number *n)
|
|
{
|
|
struct number *int_part, *fract_part;
|
|
u_int i;
|
|
|
|
if (BN_is_zero(n->number))
|
|
return n->scale ? n->scale : 1;
|
|
|
|
int_part = new_number();
|
|
fract_part = new_number();
|
|
fract_part->scale = n->scale;
|
|
split_number(n, int_part->number, fract_part->number);
|
|
|
|
i = 0;
|
|
while (!BN_is_zero(int_part->number)) {
|
|
BN_div_word(int_part->number, 10);
|
|
i++;
|
|
}
|
|
free_number(int_part);
|
|
free_number(fract_part);
|
|
return (i + n->scale);
|
|
}
|
|
|
|
static void
|
|
num_digits(void)
|
|
{
|
|
struct number *n = NULL;
|
|
struct value *value;
|
|
size_t digits;
|
|
|
|
value = pop();
|
|
if (value != NULL) {
|
|
switch (value->type) {
|
|
case BCODE_NONE:
|
|
return;
|
|
case BCODE_NUMBER:
|
|
digits = count_digits(value->u.num);
|
|
n = new_number();
|
|
bn_check(BN_set_word(n->number, digits));
|
|
break;
|
|
case BCODE_STRING:
|
|
digits = strlen(value->u.string);
|
|
n = new_number();
|
|
bn_check(BN_set_word(n->number, digits));
|
|
break;
|
|
}
|
|
stack_free_value(value);
|
|
push_number(n);
|
|
}
|
|
}
|
|
|
|
static void
|
|
to_ascii(void)
|
|
{
|
|
struct number *n;
|
|
struct value *value;
|
|
char str[2];
|
|
|
|
value = pop();
|
|
if (value != NULL) {
|
|
str[1] = '\0';
|
|
switch (value->type) {
|
|
case BCODE_NONE:
|
|
return;
|
|
case BCODE_NUMBER:
|
|
n = value->u.num;
|
|
normalize(n, 0);
|
|
if (BN_num_bits(n->number) > 8)
|
|
bn_check(BN_mask_bits(n->number, 8));
|
|
str[0] = (char)BN_get_word(n->number);
|
|
break;
|
|
case BCODE_STRING:
|
|
str[0] = value->u.string[0];
|
|
break;
|
|
}
|
|
stack_free_value(value);
|
|
push_string(bstrdup(str));
|
|
}
|
|
}
|
|
|
|
static int
|
|
readreg(void)
|
|
{
|
|
int ch1, ch2, idx;
|
|
|
|
idx = readch();
|
|
if (idx == 0xff && bmachine.extended_regs) {
|
|
ch1 = readch();
|
|
ch2 = readch();
|
|
if (ch1 == EOF || ch2 == EOF) {
|
|
warnx("unexpected eof");
|
|
idx = -1;
|
|
} else
|
|
idx = (ch1 << 8) + ch2 + UCHAR_MAX + 1;
|
|
}
|
|
if (idx < 0 || (unsigned)idx >= bmachine.reg_array_size) {
|
|
warnx("internal error: reg num = %d", idx);
|
|
idx = -1;
|
|
}
|
|
return (idx);
|
|
}
|
|
|
|
static void
|
|
load(void)
|
|
{
|
|
struct number *n;
|
|
struct value *v;
|
|
struct value copy;
|
|
int idx;
|
|
|
|
idx = readreg();
|
|
if (idx >= 0) {
|
|
v = stack_tos(&bmachine.reg[idx]);
|
|
if (v == NULL) {
|
|
n = new_number();
|
|
bn_check(BN_zero(n->number));
|
|
push_number(n);
|
|
} else
|
|
push(stack_dup_value(v, ©));
|
|
}
|
|
}
|
|
|
|
static void
|
|
store(void)
|
|
{
|
|
struct value *val;
|
|
int idx;
|
|
|
|
idx = readreg();
|
|
if (idx >= 0) {
|
|
val = pop();
|
|
if (val == NULL) {
|
|
return;
|
|
}
|
|
stack_set_tos(&bmachine.reg[idx], val);
|
|
}
|
|
}
|
|
|
|
static void
|
|
load_stack(void)
|
|
{
|
|
struct stack *stack;
|
|
struct value *value;
|
|
int idx;
|
|
|
|
idx = readreg();
|
|
if (idx >= 0) {
|
|
stack = &bmachine.reg[idx];
|
|
value = NULL;
|
|
if (stack_size(stack) > 0) {
|
|
value = stack_pop(stack);
|
|
}
|
|
if (value != NULL)
|
|
push(value);
|
|
else
|
|
warnx("stack register '%c' (0%o) is empty",
|
|
idx, idx);
|
|
}
|
|
}
|
|
|
|
static void
|
|
store_stack(void)
|
|
{
|
|
struct value *value;
|
|
int idx;
|
|
|
|
idx = readreg();
|
|
if (idx >= 0) {
|
|
value = pop();
|
|
if (value == NULL)
|
|
return;
|
|
stack_push(&bmachine.reg[idx], value);
|
|
}
|
|
}
|
|
|
|
static void
|
|
load_array(void)
|
|
{
|
|
struct number *inumber, *n;
|
|
struct stack *stack;
|
|
struct value *v;
|
|
struct value copy;
|
|
u_long idx;
|
|
int reg;
|
|
|
|
reg = readreg();
|
|
if (reg >= 0) {
|
|
inumber = pop_number();
|
|
if (inumber == NULL)
|
|
return;
|
|
idx = get_ulong(inumber);
|
|
if (BN_is_negative(inumber->number))
|
|
warnx("negative idx");
|
|
else if (idx == BN_MASK2 || idx > MAX_ARRAY_INDEX)
|
|
warnx("idx too big");
|
|
else {
|
|
stack = &bmachine.reg[reg];
|
|
v = frame_retrieve(stack, idx);
|
|
if (v == NULL || v->type == BCODE_NONE) {
|
|
n = new_number();
|
|
bn_check(BN_zero(n->number));
|
|
push_number(n);
|
|
}
|
|
else
|
|
push(stack_dup_value(v, ©));
|
|
}
|
|
free_number(inumber);
|
|
}
|
|
}
|
|
|
|
static void
|
|
store_array(void)
|
|
{
|
|
struct number *inumber;
|
|
struct value *value;
|
|
struct stack *stack;
|
|
u_long idx;
|
|
int reg;
|
|
|
|
reg = readreg();
|
|
if (reg >= 0) {
|
|
inumber = pop_number();
|
|
if (inumber == NULL)
|
|
return;
|
|
value = pop();
|
|
if (value == NULL) {
|
|
free_number(inumber);
|
|
return;
|
|
}
|
|
idx = get_ulong(inumber);
|
|
if (BN_is_negative(inumber->number)) {
|
|
warnx("negative idx");
|
|
stack_free_value(value);
|
|
} else if (idx == BN_MASK2 || idx > MAX_ARRAY_INDEX) {
|
|
warnx("idx too big");
|
|
stack_free_value(value);
|
|
} else {
|
|
stack = &bmachine.reg[reg];
|
|
frame_assign(stack, idx, value);
|
|
}
|
|
free_number(inumber);
|
|
}
|
|
}
|
|
|
|
static void
|
|
push_line(void)
|
|
{
|
|
|
|
push_string(read_string(&bmachine.readstack[bmachine.readsp]));
|
|
}
|
|
|
|
static void
|
|
comment(void)
|
|
{
|
|
|
|
free(readline());
|
|
}
|
|
|
|
static void
|
|
bexec(char *line)
|
|
{
|
|
|
|
system(line);
|
|
free(line);
|
|
}
|
|
|
|
static void
|
|
badd(void)
|
|
{
|
|
struct number *a, *b, *r;
|
|
|
|
a = pop_number();
|
|
if (a == NULL)
|
|
return;
|
|
b = pop_number();
|
|
if (b == NULL) {
|
|
push_number(a);
|
|
return;
|
|
}
|
|
|
|
r = new_number();
|
|
r->scale = max(a->scale, b->scale);
|
|
if (r->scale > a->scale)
|
|
normalize(a, r->scale);
|
|
else if (r->scale > b->scale)
|
|
normalize(b, r->scale);
|
|
bn_check(BN_add(r->number, a->number, b->number));
|
|
push_number(r);
|
|
free_number(a);
|
|
free_number(b);
|
|
}
|
|
|
|
static void
|
|
bsub(void)
|
|
{
|
|
struct number *a, *b, *r;
|
|
|
|
a = pop_number();
|
|
if (a == NULL)
|
|
return;
|
|
b = pop_number();
|
|
if (b == NULL) {
|
|
push_number(a);
|
|
return;
|
|
}
|
|
|
|
r = new_number();
|
|
|
|
r->scale = max(a->scale, b->scale);
|
|
if (r->scale > a->scale)
|
|
normalize(a, r->scale);
|
|
else if (r->scale > b->scale)
|
|
normalize(b, r->scale);
|
|
bn_check(BN_sub(r->number, b->number, a->number));
|
|
push_number(r);
|
|
free_number(a);
|
|
free_number(b);
|
|
}
|
|
|
|
void
|
|
bmul_number(struct number *r, struct number *a, struct number *b, u_int scale)
|
|
{
|
|
BN_CTX *ctx;
|
|
|
|
/* Create copies of the scales, since r might be equal to a or b */
|
|
u_int ascale = a->scale;
|
|
u_int bscale = b->scale;
|
|
u_int rscale = ascale + bscale;
|
|
|
|
ctx = BN_CTX_new();
|
|
bn_checkp(ctx);
|
|
bn_check(BN_mul(r->number, a->number, b->number, ctx));
|
|
BN_CTX_free(ctx);
|
|
|
|
r->scale = rscale;
|
|
if (rscale > bmachine.scale && rscale > ascale && rscale > bscale)
|
|
normalize(r, max(scale, max(ascale, bscale)));
|
|
}
|
|
|
|
static void
|
|
bmul(void)
|
|
{
|
|
struct number *a, *b, *r;
|
|
|
|
a = pop_number();
|
|
if (a == NULL)
|
|
return;
|
|
b = pop_number();
|
|
if (b == NULL) {
|
|
push_number(a);
|
|
return;
|
|
}
|
|
|
|
r = new_number();
|
|
bmul_number(r, a, b, bmachine.scale);
|
|
|
|
push_number(r);
|
|
free_number(a);
|
|
free_number(b);
|
|
}
|
|
|
|
static void
|
|
bdiv(void)
|
|
{
|
|
struct number *a, *b, *r;
|
|
|
|
a = pop_number();
|
|
if (a == NULL)
|
|
return;
|
|
b = pop_number();
|
|
if (b == NULL) {
|
|
push_number(a);
|
|
return;
|
|
}
|
|
|
|
r = div_number(b, a, bmachine.scale);
|
|
|
|
push_number(r);
|
|
free_number(a);
|
|
free_number(b);
|
|
}
|
|
|
|
static void
|
|
bmod(void)
|
|
{
|
|
struct number *a, *b, *r;
|
|
BN_CTX *ctx;
|
|
u_int scale;
|
|
|
|
a = pop_number();
|
|
if (a == NULL)
|
|
return;
|
|
b = pop_number();
|
|
if (b == NULL) {
|
|
push_number(a);
|
|
return;
|
|
}
|
|
|
|
r = new_number();
|
|
scale = max(a->scale, b->scale);
|
|
r->scale = max(b->scale, a->scale + bmachine.scale);
|
|
|
|
if (BN_is_zero(a->number))
|
|
warnx("remainder by zero");
|
|
else {
|
|
normalize(a, scale);
|
|
normalize(b, scale + bmachine.scale);
|
|
|
|
ctx = BN_CTX_new();
|
|
bn_checkp(ctx);
|
|
bn_check(BN_mod(r->number, b->number, a->number, ctx));
|
|
BN_CTX_free(ctx);
|
|
}
|
|
push_number(r);
|
|
free_number(a);
|
|
free_number(b);
|
|
}
|
|
|
|
static void
|
|
bdivmod(void)
|
|
{
|
|
struct number *a, *b, *rdiv, *rmod;
|
|
BN_CTX *ctx;
|
|
u_int scale;
|
|
|
|
a = pop_number();
|
|
if (a == NULL)
|
|
return;
|
|
b = pop_number();
|
|
if (b == NULL) {
|
|
push_number(a);
|
|
return;
|
|
}
|
|
|
|
rdiv = new_number();
|
|
rmod = new_number();
|
|
rdiv->scale = bmachine.scale;
|
|
rmod->scale = max(b->scale, a->scale + bmachine.scale);
|
|
scale = max(a->scale, b->scale);
|
|
|
|
if (BN_is_zero(a->number))
|
|
warnx("divide by zero");
|
|
else {
|
|
normalize(a, scale);
|
|
normalize(b, scale + bmachine.scale);
|
|
|
|
ctx = BN_CTX_new();
|
|
bn_checkp(ctx);
|
|
bn_check(BN_div(rdiv->number, rmod->number,
|
|
b->number, a->number, ctx));
|
|
BN_CTX_free(ctx);
|
|
}
|
|
push_number(rdiv);
|
|
push_number(rmod);
|
|
free_number(a);
|
|
free_number(b);
|
|
}
|
|
|
|
static void
|
|
bexp(void)
|
|
{
|
|
struct number *a, *p;
|
|
struct number *r;
|
|
bool neg;
|
|
u_int rscale;
|
|
|
|
p = pop_number();
|
|
if (p == NULL)
|
|
return;
|
|
a = pop_number();
|
|
if (a == NULL) {
|
|
push_number(p);
|
|
return;
|
|
}
|
|
|
|
if (p->scale != 0) {
|
|
BIGNUM *i, *f;
|
|
i = BN_new();
|
|
bn_checkp(i);
|
|
f = BN_new();
|
|
bn_checkp(f);
|
|
split_number(p, i, f);
|
|
if (!BN_is_zero(f))
|
|
warnx("Runtime warning: non-zero fractional part in exponent");
|
|
BN_free(i);
|
|
BN_free(f);
|
|
}
|
|
|
|
normalize(p, 0);
|
|
|
|
neg = false;
|
|
if (BN_is_negative(p->number)) {
|
|
neg = true;
|
|
negate(p);
|
|
rscale = bmachine.scale;
|
|
} else {
|
|
/* Posix bc says min(a.scale * b, max(a.scale, scale) */
|
|
u_long b;
|
|
u_int m;
|
|
|
|
b = BN_get_word(p->number);
|
|
m = max(a->scale, bmachine.scale);
|
|
rscale = a->scale * (u_int)b;
|
|
if (rscale > m || (a->scale > 0 && (b == BN_MASK2 ||
|
|
b > UINT_MAX)))
|
|
rscale = m;
|
|
}
|
|
|
|
if (BN_is_zero(p->number)) {
|
|
r = new_number();
|
|
bn_check(BN_one(r->number));
|
|
normalize(r, rscale);
|
|
} else {
|
|
u_int ascale, mscale;
|
|
|
|
ascale = a->scale;
|
|
while (!BN_is_bit_set(p->number, 0)) {
|
|
ascale *= 2;
|
|
bmul_number(a, a, a, ascale);
|
|
bn_check(BN_rshift1(p->number, p->number));
|
|
}
|
|
|
|
r = dup_number(a);
|
|
bn_check(BN_rshift1(p->number, p->number));
|
|
|
|
mscale = ascale;
|
|
while (!BN_is_zero(p->number)) {
|
|
ascale *= 2;
|
|
bmul_number(a, a, a, ascale);
|
|
if (BN_is_bit_set(p->number, 0)) {
|
|
mscale += ascale;
|
|
bmul_number(r, r, a, mscale);
|
|
}
|
|
bn_check(BN_rshift1(p->number, p->number));
|
|
}
|
|
|
|
if (neg) {
|
|
BN_CTX *ctx;
|
|
BIGNUM *one;
|
|
|
|
one = BN_new();
|
|
bn_checkp(one);
|
|
bn_check(BN_one(one));
|
|
ctx = BN_CTX_new();
|
|
bn_checkp(ctx);
|
|
scale_number(one, r->scale + rscale);
|
|
|
|
if (BN_is_zero(r->number))
|
|
warnx("divide by zero");
|
|
else
|
|
bn_check(BN_div(r->number, NULL, one,
|
|
r->number, ctx));
|
|
BN_free(one);
|
|
BN_CTX_free(ctx);
|
|
r->scale = rscale;
|
|
} else
|
|
normalize(r, rscale);
|
|
}
|
|
push_number(r);
|
|
free_number(a);
|
|
free_number(p);
|
|
}
|
|
|
|
static bool
|
|
bsqrt_stop(const BIGNUM *x, const BIGNUM *y, u_int *onecount)
|
|
{
|
|
BIGNUM *r;
|
|
bool ret;
|
|
|
|
r = BN_new();
|
|
bn_checkp(r);
|
|
bn_check(BN_sub(r, x, y));
|
|
if (BN_is_one(r))
|
|
(*onecount)++;
|
|
ret = BN_is_zero(r);
|
|
BN_free(r);
|
|
return (ret || *onecount > 1);
|
|
}
|
|
|
|
static void
|
|
bsqrt(void)
|
|
{
|
|
struct number *n, *r;
|
|
BIGNUM *x, *y;
|
|
BN_CTX *ctx;
|
|
u_int onecount, scale;
|
|
|
|
onecount = 0;
|
|
n = pop_number();
|
|
if (n == NULL)
|
|
return;
|
|
if (BN_is_zero(n->number)) {
|
|
r = new_number();
|
|
push_number(r);
|
|
} else if (BN_is_negative(n->number))
|
|
warnx("square root of negative number");
|
|
else {
|
|
scale = max(bmachine.scale, n->scale);
|
|
normalize(n, 2*scale);
|
|
x = BN_dup(n->number);
|
|
bn_checkp(x);
|
|
bn_check(BN_rshift(x, x, BN_num_bits(x)/2));
|
|
y = BN_new();
|
|
bn_checkp(y);
|
|
ctx = BN_CTX_new();
|
|
bn_checkp(ctx);
|
|
for (;;) {
|
|
bn_checkp(BN_copy(y, x));
|
|
bn_check(BN_div(x, NULL, n->number, x, ctx));
|
|
bn_check(BN_add(x, x, y));
|
|
bn_check(BN_rshift1(x, x));
|
|
if (bsqrt_stop(x, y, &onecount))
|
|
break;
|
|
}
|
|
r = bmalloc(sizeof(*r));
|
|
r->scale = scale;
|
|
r->number = y;
|
|
BN_free(x);
|
|
BN_CTX_free(ctx);
|
|
push_number(r);
|
|
}
|
|
|
|
free_number(n);
|
|
}
|
|
|
|
static void
|
|
not(void)
|
|
{
|
|
struct number *a;
|
|
|
|
a = pop_number();
|
|
if (a == NULL)
|
|
return;
|
|
a->scale = 0;
|
|
bn_check(BN_set_word(a->number, BN_get_word(a->number) ? 0 : 1));
|
|
push_number(a);
|
|
}
|
|
|
|
static void
|
|
equal(void)
|
|
{
|
|
|
|
compare(BCODE_EQUAL);
|
|
}
|
|
|
|
static void
|
|
equal_numbers(void)
|
|
{
|
|
struct number *a, *b, *r;
|
|
|
|
a = pop_number();
|
|
if (a == NULL)
|
|
return;
|
|
b = pop_number();
|
|
if (b == NULL) {
|
|
push_number(a);
|
|
return;
|
|
}
|
|
r = new_number();
|
|
bn_check(BN_set_word(r->number,
|
|
compare_numbers(BCODE_EQUAL, a, b) ? 1 : 0));
|
|
push_number(r);
|
|
}
|
|
|
|
static void
|
|
less_numbers(void)
|
|
{
|
|
struct number *a, *b, *r;
|
|
|
|
a = pop_number();
|
|
if (a == NULL)
|
|
return;
|
|
b = pop_number();
|
|
if (b == NULL) {
|
|
push_number(a);
|
|
return;
|
|
}
|
|
r = new_number();
|
|
bn_check(BN_set_word(r->number,
|
|
compare_numbers(BCODE_LESS, a, b) ? 1 : 0));
|
|
push_number(r);
|
|
}
|
|
|
|
static void
|
|
lesseq_numbers(void)
|
|
{
|
|
struct number *a, *b, *r;
|
|
|
|
a = pop_number();
|
|
if (a == NULL)
|
|
return;
|
|
b = pop_number();
|
|
if (b == NULL) {
|
|
push_number(a);
|
|
return;
|
|
}
|
|
r = new_number();
|
|
bn_check(BN_set_word(r->number,
|
|
compare_numbers(BCODE_NOT_GREATER, a, b) ? 1 : 0));
|
|
push_number(r);
|
|
}
|
|
|
|
static void
|
|
not_equal(void)
|
|
{
|
|
|
|
compare(BCODE_NOT_EQUAL);
|
|
}
|
|
|
|
static void
|
|
less(void)
|
|
{
|
|
|
|
compare(BCODE_LESS);
|
|
}
|
|
|
|
static void
|
|
not_compare(void)
|
|
{
|
|
|
|
switch (readch()) {
|
|
case '<':
|
|
not_less();
|
|
break;
|
|
case '>':
|
|
not_greater();
|
|
break;
|
|
case '=':
|
|
not_equal();
|
|
break;
|
|
default:
|
|
unreadch();
|
|
bexec(readline());
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
not_less(void)
|
|
{
|
|
|
|
compare(BCODE_NOT_LESS);
|
|
}
|
|
|
|
static void
|
|
greater(void)
|
|
{
|
|
|
|
compare(BCODE_GREATER);
|
|
}
|
|
|
|
static void
|
|
not_greater(void)
|
|
{
|
|
|
|
compare(BCODE_NOT_GREATER);
|
|
}
|
|
|
|
static bool
|
|
compare_numbers(enum bcode_compare type, struct number *a, struct number *b)
|
|
{
|
|
u_int scale;
|
|
int cmp;
|
|
|
|
scale = max(a->scale, b->scale);
|
|
|
|
if (scale > a->scale)
|
|
normalize(a, scale);
|
|
else if (scale > b->scale)
|
|
normalize(b, scale);
|
|
|
|
cmp = BN_cmp(a->number, b->number);
|
|
|
|
free_number(a);
|
|
free_number(b);
|
|
|
|
switch (type) {
|
|
case BCODE_EQUAL:
|
|
return (cmp == 0);
|
|
case BCODE_NOT_EQUAL:
|
|
return (cmp != 0);
|
|
case BCODE_LESS:
|
|
return (cmp < 0);
|
|
case BCODE_NOT_LESS:
|
|
return (cmp >= 0);
|
|
case BCODE_GREATER:
|
|
return (cmp > 0);
|
|
case BCODE_NOT_GREATER:
|
|
return (cmp <= 0);
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
static void
|
|
compare(enum bcode_compare type)
|
|
{
|
|
struct number *a, *b;
|
|
struct value *v;
|
|
int idx, elseidx;
|
|
bool ok;
|
|
|
|
elseidx = NO_ELSE;
|
|
idx = readreg();
|
|
if (readch() == 'e')
|
|
elseidx = readreg();
|
|
else
|
|
unreadch();
|
|
|
|
a = pop_number();
|
|
if (a == NULL)
|
|
return;
|
|
b = pop_number();
|
|
if (b == NULL) {
|
|
push_number(a);
|
|
return;
|
|
}
|
|
|
|
ok = compare_numbers(type, a, b);
|
|
|
|
if (!ok && elseidx != NO_ELSE)
|
|
idx = elseidx;
|
|
|
|
if (idx >= 0 && (ok || (!ok && elseidx != NO_ELSE))) {
|
|
v = stack_tos(&bmachine.reg[idx]);
|
|
if (v == NULL)
|
|
warnx("register '%c' (0%o) is empty", idx, idx);
|
|
else {
|
|
switch(v->type) {
|
|
case BCODE_NONE:
|
|
warnx("register '%c' (0%o) is empty", idx, idx);
|
|
break;
|
|
case BCODE_NUMBER:
|
|
warn("eval called with non-string argument");
|
|
break;
|
|
case BCODE_STRING:
|
|
eval_string(bstrdup(v->u.string));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
nop(void)
|
|
{
|
|
|
|
}
|
|
|
|
static void
|
|
quit(void)
|
|
{
|
|
|
|
if (bmachine.readsp < 2)
|
|
exit(0);
|
|
src_free();
|
|
bmachine.readsp--;
|
|
src_free();
|
|
bmachine.readsp--;
|
|
}
|
|
|
|
static void
|
|
quitN(void)
|
|
{
|
|
struct number *n;
|
|
u_long i;
|
|
|
|
n = pop_number();
|
|
if (n == NULL)
|
|
return;
|
|
i = get_ulong(n);
|
|
free_number(n);
|
|
if (i == BN_MASK2 || i == 0)
|
|
warnx("Q command requires a number >= 1");
|
|
else if (bmachine.readsp < i)
|
|
warnx("Q command argument exceeded string execution depth");
|
|
else {
|
|
while (i-- > 0) {
|
|
src_free();
|
|
bmachine.readsp--;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
skipN(void)
|
|
{
|
|
struct number *n;
|
|
u_long i;
|
|
|
|
n = pop_number();
|
|
if (n == NULL)
|
|
return;
|
|
i = get_ulong(n);
|
|
if (i == BN_MASK2)
|
|
warnx("J command requires a number >= 0");
|
|
else if (i > 0 && bmachine.readsp < i)
|
|
warnx("J command argument exceeded string execution depth");
|
|
else {
|
|
while (i-- > 0) {
|
|
src_free();
|
|
bmachine.readsp--;
|
|
}
|
|
skip_until_mark();
|
|
}
|
|
}
|
|
|
|
static void
|
|
skip_until_mark(void)
|
|
{
|
|
|
|
for (;;) {
|
|
switch (readch()) {
|
|
case 'M':
|
|
return;
|
|
case EOF:
|
|
errx(1, "mark not found");
|
|
return;
|
|
case 'l':
|
|
case 'L':
|
|
case 's':
|
|
case 'S':
|
|
case ':':
|
|
case ';':
|
|
case '<':
|
|
case '>':
|
|
case '=':
|
|
readreg();
|
|
if (readch() == 'e')
|
|
readreg();
|
|
else
|
|
unreadch();
|
|
break;
|
|
case '[':
|
|
free(read_string(&bmachine.readstack[bmachine.readsp]));
|
|
break;
|
|
case '!':
|
|
switch (readch()) {
|
|
case '<':
|
|
case '>':
|
|
case '=':
|
|
readreg();
|
|
if (readch() == 'e')
|
|
readreg();
|
|
else
|
|
unreadch();
|
|
break;
|
|
default:
|
|
free(readline());
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
parse_number(void)
|
|
{
|
|
|
|
unreadch();
|
|
push_number(readnumber(&bmachine.readstack[bmachine.readsp],
|
|
bmachine.ibase, bmachine.scale));
|
|
}
|
|
|
|
static void
|
|
unknown(void)
|
|
{
|
|
int ch = bmachine.readstack[bmachine.readsp].lastchar;
|
|
warnx("%c (0%o) is unimplemented", ch, ch);
|
|
}
|
|
|
|
static void
|
|
eval_string(char *p)
|
|
{
|
|
int ch;
|
|
|
|
if (bmachine.readsp > 0) {
|
|
/* Check for tail call. Do not recurse in that case. */
|
|
ch = readch();
|
|
if (ch == EOF) {
|
|
src_free();
|
|
src_setstring(&bmachine.readstack[bmachine.readsp], p);
|
|
return;
|
|
} else
|
|
unreadch();
|
|
}
|
|
if (bmachine.readsp == bmachine.readstack_sz - 1) {
|
|
size_t newsz = bmachine.readstack_sz * 2;
|
|
struct source *stack;
|
|
stack = reallocarray(bmachine.readstack, newsz,
|
|
sizeof(struct source));
|
|
if (stack == NULL)
|
|
err(1, "recursion too deep");
|
|
bmachine.readstack_sz = newsz;
|
|
bmachine.readstack = stack;
|
|
}
|
|
src_setstring(&bmachine.readstack[++bmachine.readsp], p);
|
|
}
|
|
|
|
static void
|
|
eval_line(void)
|
|
{
|
|
/* Always read from stdin */
|
|
struct source in;
|
|
char *p;
|
|
|
|
clearerr(stdin);
|
|
src_setstream(&in, stdin);
|
|
p = (*in.vtable->readline)(&in);
|
|
eval_string(p);
|
|
}
|
|
|
|
static void
|
|
eval_tos(void)
|
|
{
|
|
char *p;
|
|
|
|
p = pop_string();
|
|
if (p != NULL)
|
|
eval_string(p);
|
|
}
|
|
|
|
void
|
|
eval(void)
|
|
{
|
|
int ch;
|
|
|
|
for (;;) {
|
|
ch = readch();
|
|
if (ch == EOF) {
|
|
if (bmachine.readsp == 0)
|
|
return;
|
|
src_free();
|
|
bmachine.readsp--;
|
|
continue;
|
|
}
|
|
#ifdef DEBUGGING
|
|
fprintf(stderr, "# %c\n", ch);
|
|
stack_print(stderr, &bmachine.stack, "* ",
|
|
bmachine.obase);
|
|
fprintf(stderr, "%zd =>\n", bmachine.readsp);
|
|
#endif
|
|
|
|
if (0 <= ch && ch < (signed)UCHAR_MAX)
|
|
(*jump_table[ch])();
|
|
else
|
|
warnx("internal error: opcode %d", ch);
|
|
|
|
#ifdef DEBUGGING
|
|
stack_print(stderr, &bmachine.stack, "* ",
|
|
bmachine.obase);
|
|
fprintf(stderr, "%zd ==\n", bmachine.readsp);
|
|
#endif
|
|
}
|
|
}
|