Print changes
This commit is contained in:
+186
-9
@@ -2,24 +2,197 @@
|
||||
|
||||
#include "lisp.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
DEFVAR(print_circular, "print-circular", "", Qt);
|
||||
DEFVAR(print_length, "print-length", "", MAKE_FIXNUM(100));
|
||||
DEFVAR(print_level, "print-level", "", Qnil);
|
||||
DEFVAR(print_base, "print-base", "", MAKE_FIXNUM(10));
|
||||
DEFVAR(print_base_upper, "print-base-upper", "", Qt);
|
||||
DEFVAR(print_precision, "print-precision", "", MAKE_FIXNUM(6));
|
||||
|
||||
struct PrintContext {
|
||||
DEFUN(write_byte, "write-byte", (LispVal * ch), "(ch)", "") {
|
||||
if (NILP(ch)) {
|
||||
fflush(stdout);
|
||||
}
|
||||
CHECK_TYPE(ch, TYPE_FIXNUM);
|
||||
fixnum_t f = XFIXNUM(ch);
|
||||
if (f < 0 || f > 255) {
|
||||
// TODO error
|
||||
abort();
|
||||
}
|
||||
fputc(f, stdout);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
struct PrintOptions {
|
||||
bool readable;
|
||||
bool circle;
|
||||
bool length;
|
||||
fixnum_t length;
|
||||
fixnum_t level;
|
||||
fixnum_t base;
|
||||
bool base_upper;
|
||||
fixnum_t precision;
|
||||
};
|
||||
|
||||
static void init_print_context(struct PrintContext *restrict pc) {
|
||||
pc->circle = true;
|
||||
pc->length = 80;
|
||||
struct PrintContext {
|
||||
struct PrintOptions opts;
|
||||
LispVal *print_char_fun;
|
||||
LispHashTable *seen_objects;
|
||||
LispVal *length_stack;
|
||||
};
|
||||
|
||||
static void init_print_options(struct PrintOptions *opts, bool readable) {
|
||||
opts->readable = readable;
|
||||
opts->circle = !NILP(Vprint_circular);
|
||||
if (NILP(Vprint_length)) {
|
||||
opts->length = SIZE_MAX;
|
||||
} else {
|
||||
CHECK_TYPE(Vprint_length, TYPE_FIXNUM);
|
||||
opts->length = XFIXNUM(Vprint_length);
|
||||
}
|
||||
if (NILP(Vprint_level)) {
|
||||
opts->level = SIZE_MAX;
|
||||
} else {
|
||||
CHECK_TYPE(Vprint_level, TYPE_FIXNUM);
|
||||
opts->level = XFIXNUM(Vprint_level);
|
||||
}
|
||||
CHECK_TYPE(Vprint_base, TYPE_FIXNUM);
|
||||
opts->base = XFIXNUM(Vprint_base);
|
||||
if (opts->base < 2 || opts->base > 16) {
|
||||
opts->base = 10;
|
||||
}
|
||||
opts->base_upper = !NILP(Vprint_base_upper);
|
||||
CHECK_TYPE(Vprint_precision, TYPE_FIXNUM);
|
||||
opts->precision = XFIXNUM(Vprint_precision);
|
||||
if (opts->precision < 0) {
|
||||
opts->precision = 0;
|
||||
} else if (opts->precision > LISP_FLOAT_MAX_PRECISION) {
|
||||
opts->precision = LISP_FLOAT_MAX_PRECISION;
|
||||
}
|
||||
}
|
||||
|
||||
static void init_print_context(struct PrintContext *restrict pc, bool readable,
|
||||
LispVal *print_char_fun) {
|
||||
init_print_options(&pc->opts, readable);
|
||||
if (NILP(print_char_fun)) {
|
||||
pc->print_char_fun = Qwrite_byte;
|
||||
} else {
|
||||
pc->print_char_fun = print_char_fun;
|
||||
}
|
||||
pc->seen_objects = Fmake_hash_table(Qnil, Qnil);
|
||||
pc->length_stack = Qnil;
|
||||
}
|
||||
|
||||
static void print_char(struct PrintContext *restrict pc, char c) {
|
||||
CALL(pc->print_char_fun, MAKE_FIXNUM(c));
|
||||
}
|
||||
|
||||
static void print_buffer(struct PrintContext *restrict pc,
|
||||
const char *restrict buf, size_t len) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
print_char(pc, buf[i]);
|
||||
}
|
||||
}
|
||||
#define PRINT_STATIC_BUFFER(pc, buf) print_buffer(pc, buf, sizeof(buf) - 1)
|
||||
|
||||
static void print_fixnum_base(struct PrintContext *restrict pc, LispVal *val) {
|
||||
switch (pc->opts.base) {
|
||||
case 2:
|
||||
print_char(pc, '2');
|
||||
break;
|
||||
case 8:
|
||||
print_char(pc, '8');
|
||||
break;
|
||||
case 10:
|
||||
print_char(pc, '1');
|
||||
print_char(pc, '0');
|
||||
break;
|
||||
case 16:
|
||||
print_char(pc, '1');
|
||||
print_char(pc, '6');
|
||||
break;
|
||||
default:
|
||||
// TODO error
|
||||
abort();
|
||||
}
|
||||
print_char(pc, '#');
|
||||
}
|
||||
|
||||
static void print_fixnum(struct PrintContext *restrict pc, LispVal *val) {
|
||||
fixnum_t fn = XFIXNUM(val);
|
||||
if (fn == 0) {
|
||||
Ffuncall(pc->print_char_fun, MAKE_FIXNUM('0'));
|
||||
} else {
|
||||
if (pc->opts.base != 10 && pc->opts.readable) {
|
||||
print_fixnum_base(pc, val);
|
||||
}
|
||||
if (fn < 0) {
|
||||
fn = -fn;
|
||||
print_char(pc, '-');
|
||||
}
|
||||
// smallest base is 2
|
||||
fixnum_t base = pc->opts.base;
|
||||
char buf[64];
|
||||
size_t num_len = 0;
|
||||
while (fn) {
|
||||
fixnum_t digit = fn % base;
|
||||
fn /= base;
|
||||
char to_print;
|
||||
if (digit >= 0 && digit <= 9) {
|
||||
to_print = '0' + digit;
|
||||
} else if (digit >= 10 && digit <= 15) {
|
||||
to_print = (pc->opts.base_upper ? 'A' : 'a') + digit - 10;
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
buf[63 - (num_len++)] = to_print;
|
||||
}
|
||||
print_buffer(pc, &buf[64 - num_len], num_len);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_float(struct PrintContext *restrict pc, LispVal *val) {
|
||||
lisp_float_t fv = XLISP_FLOAT(val);
|
||||
switch (fpclassify(fv)) {
|
||||
case FP_ZERO:
|
||||
PRINT_STATIC_BUFFER(pc, "0.0");
|
||||
break;
|
||||
case FP_NAN:
|
||||
PRINT_STATIC_BUFFER(pc, "0.0eNaN");
|
||||
break;
|
||||
case FP_INFINITE:
|
||||
if (fv < 0.0) {
|
||||
PRINT_STATIC_BUFFER(pc, "0.0eInf");
|
||||
} else {
|
||||
PRINT_STATIC_BUFFER(pc, "-0.0eInf");
|
||||
}
|
||||
break;
|
||||
case FP_SUBNORMAL:
|
||||
case FP_NORMAL: {
|
||||
char fmt[16];
|
||||
int written =
|
||||
snprintf(fmt, sizeof(fmt), "%%%" LISP_FIXNUM_PRINTF(d) "g",
|
||||
pc->opts.precision);
|
||||
assert(written < sizeof(fmt));
|
||||
char buffer[32];
|
||||
written = snprintf(buffer, sizeof(buffer), fmt, buffer);
|
||||
assert(written < sizeof(buffer));
|
||||
print_buffer(pc, buffer, written);
|
||||
} break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void print_driver(struct PrintContext *restrict pc, LispVal *val) {
|
||||
switch (TYPE_OF(val)) {
|
||||
case TYPE_FIXNUM:
|
||||
print_fixnum(pc, val);
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
print_float(pc, val);
|
||||
break;
|
||||
case TYPE_CONS:
|
||||
case TYPE_STRING:
|
||||
case TYPE_SYMBOL:
|
||||
@@ -32,16 +205,20 @@ static void print_driver(struct PrintContext *restrict pc, LispVal *val) {
|
||||
}
|
||||
}
|
||||
|
||||
DEFUN(princ, "princ", (LispVal * val), "(val)", "") {
|
||||
// Not readable
|
||||
DEFUN(princ, "princ", (LispVal * val, LispVal *print_char_fun),
|
||||
"(val &optional print-char-fun)", "") {
|
||||
struct PrintContext pc;
|
||||
init_print_context(&pc);
|
||||
init_print_context(&pc, false, print_char_fun);
|
||||
print_driver(&pc, val);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
DEFUN(prin1, "prin1", (LispVal * val), "(val)", "") {
|
||||
// Readable
|
||||
DEFUN(prin1, "prin1", (LispVal * val, LispVal *print_char_fun),
|
||||
"(val &optional print-char-fun)", "") {
|
||||
struct PrintContext pc;
|
||||
init_print_context(&pc);
|
||||
init_print_context(&pc, true, print_char_fun);
|
||||
print_driver(&pc, val);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user