#include "inc/priv.h" #include "inc/str.h" #include "inc/bitvector.h" #include "inc/operators.h" #include "inc/cvalues.h" #include "inc/ieee754.h" #include "inc/print.h" #include "inc/read.h" enum { SMALL_STR_LEN = 24, }; #define LOG2_10 3.321928094887362347870319429489 #define cons_index(c) (ptr(sl_aligned, c)-(sl_aligned*)slvm.fromspace) #define ismarked(c) bitvector_get(sl.consflags, cons_index(c)) #define mark_cons(c) bitvector_set(sl.consflags, cons_index(c)) #define unmark_cons(c) bitvector_reset(sl.consflags, cons_index(c)) sl_v sl_printreadablysym, sl_printprettysym; static sl_v rmarginsym, circlesym; static sl_v lengthsym, levelsym, widthsym; static sl_v radixsym, basesym; static sl_v ptrradixsym, ptrbasesym; static sl_v syntaxsugarsym; static inline void outc(char c) { if(sl_unlikely(ios_putc(sl.pr.f, c) != 1)) cthrow(error(sl_errio, "write failed"), c); if(sl_likely(c != '\n')) sl.pr.x += sl_wcwidth(c); else sl.pr.x = 0; } sl_nonnull(1) static inline void outsn(const char *s, usize n) { if(sl_unlikely(ios_write(sl.pr.f, s, n) != n)) cthrow(error(sl_errio, "write failed"), s); const char *nl = sl_memrchr(s, '\n', n); bool err; if(sl_likely(nl == nil)) sl.pr.x += u8_strwidth(s, n, &err); else if(sl_unlikely((nl+1-s) < (intptr)n)) sl.pr.x = u8_strwidth(nl+1, s+n-nl-1, &err); } sl_nonnull(1) static inline void outs(const char *s) { outsn(s, strlen(s)); } #define outsc(s) do{ \ const char vs[] = s; \ outsn(vs, sizeof(vs)-1); \ }while(0) static int outindent(int n) { // move back to left margin if we get too indented if(sl.pr.rmargin >= 0 && n > sl.pr.rmargin-12) n = 2; int n0 = n; if(sl_unlikely(ios_putc(sl.pr.f, '\n') != 1)) goto err; sl.pr.y++; sl.pr.x = n; while(n){ if(sl_unlikely(ios_putc(sl.pr.f, ' ') != 1)) goto err; n--; } return n0; err: cthrow(error(sl_errio, "write failed"), n); } static int print_radix_prefix(bool radix, int base) { if(!radix) return 0; switch(base){ case 2: outsc("#b"); return 0; case 8: outsc("#o"); return 0; case 10: outsc("#d"); return 0; case 16: outsc("#x"); return 0; default: return ios_printf(sl.pr.f, "#%dr", base); } } static int print_num(u64int x, int base) { char tmp[64+1]; if(sl_unlikely(base == 32)){ u8int d[8] = {x>>56, x>>48, x>>40, x>>32, x>>24, x>>16, x>>8, x}; int n; for(n = 0; n < 7 && d[n] == 0; n++); n = enc32(tmp, sizeof(tmp), d+n, sizeof(d)-n); return n > 0 ? (int)ios_write(sl.pr.f, tmp, n) : 0; } char *s = uint2str(tmp, sizeof(tmp), x, base); return ios_write(sl.pr.f, s, tmp+sizeof(tmp)-1-s); } sl_nonnull(1) static void print_str(const char *str, usize sz) { char buf[64]; u8int c; static const char hexdig[] = "0123456789abcdef"; usize i = 0; if(!u8_isvalid(str, sz)){ // alternate print algorithm that preserves data if it's not UTF-8 for(; i < sz; i++){ c = str[i]; if(sl_unlikely(c == '\\')) outsc("\\\\"); else if(sl_unlikely(c == '"')) outsc("\\\""); else if(sl_likely(c >= 32 && c < 0x7f)) outc(c); else{ outsc("\\x"); outc(hexdig[c>>4]); outc(hexdig[c&0xf]); } } }else{ while(i < sz){ usize n = u8_escape(buf, sizeof(buf), str, &i, sz, true, false); outsn(buf, n-1); } } } void sl_print_chr(char c) { outc(c); } void sl_print_str(const char *s, bool princ) { if(sl_likely(princ)) outs(s); else print_str(s, strlen(s)); } void sl_print_traverse(sl_v v) { sl_v *bp; while(iscons(v)){ if(ismarked(v)){ bp = (sl_v*)ptrhash_bp(&sl.pr.conses, (void*)v); if(*bp == (sl_v)HT_NOTFOUND) *bp = fixnum(sl.pr.label++); return; } mark_cons(v); sl_print_traverse(car_(v)); v = cdr_(v); } if(!ismanaged(v) || issym(v)) return; if(ismarked(v)){ bp = (sl_v*)ptrhash_bp(&sl.pr.conses, (void*)v); if(*bp == (sl_v)HT_NOTFOUND) *bp = fixnum(sl.pr.label++); }else if(isvec(v)){ if(vec_size(v) > 0) mark_cons(v); for(int i = 0; i < vec_size(v); i++) sl_print_traverse(vec_elt(v, i)); }else if(isfn(v)){ mark_cons(v); sl_fn *f = ptr(sl_fn, v); sl_print_traverse(f->bcode); sl_print_traverse(f->vals); sl_print_traverse(f->env); }else if(iscvalue(v)){ sl_cv *cv = ptr(sl_cv, v); // don't consider shared references to "" and numbers if(!valid_numtype(cv_numtype(cv)) && (!cv_isstr(cv) || cv_len(cv) != 0)) mark_cons(v); const sl_type *t = cv_class(cv); if(t->vtable != nil && t->vtable->print_traverse != nil) t->vtable->print_traverse(v); } } sl_nonnull(1) static void print_sym_name(const char *name) { int i; bool escape = false, charescape = false; if((name[0] == '\0') || (name[0] == '.' && name[1] == '\0') || (name[0] == '#') || sl_read_numtok(name, nil, 0)) escape = true; i = 0; while(name[i]){ if(sl_unlikely(!symchar(name[i]))){ escape = true; if(name[i] == '|' || name[i] == '\\'){ charescape = true; break; } } i++; } if(sl_unlikely(escape)){ if(charescape){ outc('|'); i = 0; while(name[i]){ if(name[i] == '|' || name[i] == '\\') outc('\\'); outc(name[i]); i++; } outc('|'); }else{ outc('|'); outs(name); outc('|'); } }else{ outs(name); } } /* The following implements a simple pretty-printing algorithm. This is an unlimited-width approach that doesn't require an extra pass. It uses some heuristics to guess whether an expression is "small", and avoids wrapping symbols across lines. The result is high performance and nice output for typical code. Quality is poor for pathological or deeply-nested expressions, but those are difficult to print anyway. */ static inline int tinyp(sl_v v) { if(issym(v)){ const char *s = sym_name(v); bool err; return u8_strwidth(s, strlen(s), &err) < SMALL_STR_LEN; } if(isstr(v)) return cvalue_len(v) < SMALL_STR_LEN; if(iscvalue(v)) return valid_numtype(cvalue_numtype(v)); return isfixnum(v) || isunboxed(v) || isbuiltin(v) || isespecial(v); } static bool smallp(sl_v v) { if(tinyp(v)) return true; if(sl_unlikely(sl_isnum(v))) return true; if(iscons(v)){ if(tinyp(car_(v)) && (tinyp(cdr_(v)) || (iscons(cdr_(v)) && tinyp(car_(cdr_(v))) && cdr_(cdr_(v)) == sl_nil))) return true; return false; } if(isvec(v)){ usize s = vec_size(v); return ( s == 0 || (tinyp(vec_elt(v, 0)) && (s == 1 || (s == 2 && tinyp(vec_elt(v, 1))))) ); } return false; } static int specialindent(sl_v head) { // indent these forms 2 spaces, not lined up with the first argument if(head == sl_lambda || head == sl_trycatch || head == sl_defsym || head == sl_defmacrosym || head == sl_forsym) return 2; return -1; } static int lengthestimate(sl_v v) { // get the width of an expression if we can do so cheaply if(issym(v)){ const char *s = sym_name(v); bool err; return u8_strwidth(s, strlen(s), &err); } if(sl_isrune(v)) return 4; return -1; } static int allsmallp(sl_v v) { int n = 1; while(iscons(v)){ if(!smallp(car_(v))) return 0; v = cdr_(v); n++; if(n > 25) return n; } return n; } static int indentafter3(sl_v head, sl_v v) { // for certain X always indent (X a b c) after b return head == sl_forsym && !allsmallp(cdr_(v)); } static int indentafter2(sl_v head, sl_v v) { // for certain X always indent (X a b) after a return (head == sl_defsym || head == sl_defmacrosym) && !allsmallp(cdr_(v)); } static bool indentevery(sl_v v) { // indent before every subform of a special form, unless every // subform is "small" sl_v c = car_(v); if(sl_unlikely(c == sl_lambda || c == sl_setqsym)) return false; //if(c == sl.IF) // TODO: others // return !allsmallp(cdr_(v)); return false; } static bool blockindent(sl_v v) { // in this case we switch to block indent mode, where the head // is no longer considered special: // (a b c d e // f g h i j) return allsmallp(v) > 9; } static void print_cons(sl_v v) { const char *op; bool circle = sl.pr.circle; if(sl.pr.syntax_sugar && iscons(cdr_(v)) && cdr_(cdr_(v)) == sl_nil && !ptrhash_has(&sl.pr.conses, (void*)cdr_(v)) && ((car_(v) == sl_quote && (op = "'")) || (car_(v) == sl_backquote && (op = "`")) || (car_(v) == sl_comma && (op = ",")) || (car_(v) == sl_commaat && (op = ",@")) || (car_(v) == sl_commadot && (op = ",.")))){ // special prefix syntax if(circle){ unmark_cons(v); unmark_cons(cdr_(v)); } outs(op); sl_print_child(car_(cdr_(v))); return; } int startpos = sl.pr.x; outc('('); int newindent = sl.pr.x; int lastv, n = 0, si, ind, est, nextsmall, thistiny; bool always = false, blk = blockindent(v); if(sl_likely(!blk)) always = indentevery(v); sl_v head = car_(v); int after3 = indentafter3(head, v); int after2 = indentafter2(head, v); int n_unindented = 1; sl_v cd; while(1){ cd = cdr_(v); if(sl_unlikely(sl.pr.length >= 0 && n >= sl.pr.length)){ outsc("...)"); break; } lastv = sl.pr.y; if(circle) unmark_cons(v); sl_print_child(car_(v)); if(!iscons(cd) || ptrhash_has(&sl.pr.conses, (void*)cd)){ if(cd != sl_nil){ outsc(" . "); sl_print_child(cd); } outc(')'); break; } if(!sl.pr.pretty || (head == sl_lambda && n == 0)){ // never break line before lambda-list ind = 0; }else{ est = lengthestimate(car_(cd)); nextsmall = smallp(car_(cd)); thistiny = tinyp(car_(v)); ind = ((sl.pr.y > lastv || (sl.pr.rmargin >= 0 && sl.pr.x > sl.pr.rmargin/2 && !nextsmall && !thistiny && n>0)) || (sl.pr.rmargin >= 0 && sl.pr.x > sl.pr.rmargin-4) || (sl.pr.rmargin >= 0 && est != -1 && sl.pr.x+est > sl.pr.rmargin-2) || (head == sl_lambda && !nextsmall) || (n > 0 && always) || (n == 2 && after3) || (n == 1 && after2) || (n_unindented >= 3 && !nextsmall) || (n == 0 && !smallp(head))); } if(ind){ newindent = outindent(newindent); n_unindented = 1; }else{ n_unindented++; outc(' '); if(n == 0){ // set indent level after printing head si = specialindent(head); if(si != -1) newindent = startpos + si; else if(!blk) newindent = sl.pr.x; } } n++; v = cd; } } static void unboxed_print(sl_v v); static void cvalue_print(sl_v v); static void rune_print(Rune r); static bool print_circle_prefix(sl_v v) { sl_v label; if((label = (sl_v)ptrhash_get(&sl.pr.conses, (void*)v)) != (sl_v)HT_NOTFOUND){ if(!ismarked(v)){ int n = ios_printf(sl.pr.f, "#%"PRIdPTR"#", (intptr)numval(label)); if(sl_unlikely(n < 1)) cthrow(error(sl_errio, "write failed"), v); sl.pr.x += n; return true; } int n = ios_printf(sl.pr.f, "#%"PRIdPTR"=", (intptr)numval(label)); if(sl_unlikely(n < 1)) cthrow(error(sl_errio, "write failed"), v); sl.pr.x += n; } if(ismanaged(v)) unmark_cons(v); return false; } void sl_print_child(sl_v v) { const char *name; if(sl_unlikely(sl.pr.level >= 0 && sl.pr.p_level >= sl.pr.level && (iscons(v) || isvec(v) || isfn(v)))){ outc('#'); return; } sl.pr.p_level++; int n; s64int sx; switch(tag(v)){ case TAG_FIXNUM: n = print_radix_prefix(sl.pr.radix, sl.pr.base); if(n >= 0){ sl.pr.x += n; sx = numval(v); if(sx < 0){ outc('-'); sx = -sx; } n = print_num(sx, sl.pr.base); } if(sl_unlikely(n < 1)) cthrow(error(sl_errio, "write failed"), v); sl.pr.x += n; break; case TAG_UNBOXED: unboxed_print(v); break; case TAG_SYM: name = sym_name(v); if(sl.pr.princ) outs(name); else if(sl_unlikely(ismanaged(v))){ outsc("#:"); outs(name); }else print_sym_name(name); break; case TAG_ESPECIAL: if(v == sl_t) outc('T'); else if(v == sl_nil) outsc("NIL"); else if(v == sl_eof) outsc("#"); else if(v == sl_void) outsc("#"); else if(v == sl_unbound) outsc("#"); else abort(); break; case TAG_FN: if(isbuiltin(v)){ if(!sl.pr.princ) outsc("#."); outs(builtins[u32numval(v)].name); }else{ assert(isfn(v)); if(!sl.pr.princ){ if(sl.pr.circle && print_circle_prefix(v)) break; sl_fn *fn = ptr(sl_fn, v); outs("#fn("); char *data = cvalue_data(fn->bcode); usize i, sz = cvalue_len(fn->bcode); for(i = 0; i < sz; i++) data[i] += 48; sl_print_child(fn->bcode); for(i = 0; i < sz; i++) data[i] -= 48; if(fn->vals != sl_emptyvec || fn->env != sl_nil){ bool t = sl.pr.fnname; sl.pr.fnname = true; outc(' '); sl_print_child(fn->vals); if(fn->env != sl_nil){ outc(' '); sl_print_child(fn->env); } sl.pr.fnname = t; } if(fn->name != sl_lambda && sl.pr.fnname){ outc(' '); sl_print_child(fn->name); } outc(' '); sl_print_child(fixnum(fn->maxstack)); outc(')'); }else{ outs("#"); } } break; case TAG_CVALUE: case TAG_VEC: case TAG_CONS: if(sl.pr.circle && print_circle_prefix(v)) break; if(isvec(v)){ outs(isstruct(v) ? "#S(" : "#("); int newindent = sl.pr.x, est; int i, sz = vec_size(v); for(i = 0; i < sz; i++){ if(sl.pr.length >= 0 && i >= sl.pr.length){ outsc("..."); break; } sl_print_child(vec_elt(v, i)); if(i < sz-1){ if(!sl.pr.pretty) outc(' '); else{ est = lengthestimate(vec_elt(v, i+1)); if(sl.pr.rmargin >= 0 && ( (sl.pr.x > sl.pr.rmargin-4) || (est != -1 && (sl.pr.x+est > sl.pr.rmargin-2)) || (sl.pr.x > sl.pr.rmargin/2 && !smallp(vec_elt(v, i+1)) && !tinyp(vec_elt(v, i))))) newindent = outindent(newindent); else outc(' '); } } } outc(')'); break; } if(iscvalue(v)) cvalue_print(v); else print_cons(v); break; } sl.pr.p_level--; } static int double_exponent(double d) { union ieee754_double dl; dl.d = d; return dl.ieee.exponent - IEEE754_DOUBLE_BIAS; } sl_nonnull(1) sl_wr(1, 2) static void snprint_real(char *s, usize cnt, double r, int width, // printf field width, or 0 int dec, // # decimal digits desired, recommend 16 // # of zeros in .00...0x before using scientific notation // recommend 3-4 or so int max_digs_rt, // # of digits left of decimal before scientific notation // recommend 10 int max_digs_lf) { bool keepz = false; int mag, sz; s[0] = '\0'; if(width == -1){ width = 0; keepz = true; } if(isinf(r)){ strncpy(s, signbit(r) ? "-inf" : "inf", cnt); return; } if(isnan(r)){ strncpy(s, signbit(r) ? "-nan" : "nan", cnt); return; } if(r == 0){ strncpy(s, "0", cnt); return; } char num_format[4]; num_format[0] = 'l'; num_format[2] = '\0'; mag = double_exponent(r); mag = (int)(((double)mag)/LOG2_10 + 0.5); double fpart, temp; if(mag > max_digs_lf-1 || mag < -max_digs_rt){ num_format[1] = 'e'; temp = r/pow(10, mag); /* see if number will have a decimal */ fpart = temp - floor(temp); /* when written in scientific notation */ }else{ num_format[1] = 'f'; fpart = r - floor(r); } if(fpart == 0) dec = 0; char format[28]; if(width == 0) snprintf(format, sizeof(format), "%%.%d%s", dec, num_format); else snprintf(format, sizeof(format), "%%%d.%d%s", width, dec, num_format); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" sz = snprintf(s, cnt, format, r); #pragma GCC diagnostic pop /* trim trailing zeros from fractions. not when using scientific notation, since we might have e.g. 1.2000e+100. also not when we need a specific output width */ if(width == 0 && !keepz){ if(sz > 2 && fpart){ char *e = nil; if(num_format[1] == 'e'){ while(s[--sz] != 'e'); e = s + sz--; } while(s[sz-1] == '0'){ s[sz-1] = '\0'; sz--; } // don't need trailing . if(s[sz-1] == '.') s[--sz] = '\0'; if(num_format[1] == 'e'){ while(*e) s[sz++] = *e++; s[sz] = 0; } } } } static void rune_print(Rune r) { char seq[UTFmax+1]; int n, nb = runetochar(seq, &r); seq[nb] = '\0'; if(sl.pr.princ){ outsn(seq, nb); }else{ outsc("#\\"); if(r < nelem(ascii2name)) outs(ascii2name[r]); else if(r == 0x7f) outsc("delete"); else{ if(sl_iswprint(r)) outs(seq); else{ n = ios_printf(sl.pr.f, "x%04"PRIx32, r); if(n < 1) goto err; sl.pr.x += n; } } } return; err: cthrow(error(sl_errio, "write failed"), r); } sl_nonnull(1) static usize utf8_len4width(const char *data, usize n) { if(sl.pr.width < 0) return n; ssize w; bool err; usize len = u8_strnlen(data, n, sl.pr.width, &w, &err); return err ? n : len; } sl_nonnull(1) static usize runes_cnt4width(const void *data, usize n, usize elsize) { if(sl.pr.width < 0) return n; ssize maxwidth = sl.pr.width, totw = 0, clw = 0; usize cnt = 0, i; Rune lastr = Runeerror; bool skip = false, incluster = false; for(i = 0; i < n; i++){ Rune r = *(const Rune*)((const char*)data + i*elsize); bool boundary = !skip && !(r == Runevs16 && lastr != Runeerror); if(boundary && incluster){ if(totw + clw > maxwidth) break; totw += clw; cnt = i; clw = 0; } if(boundary) incluster = true; int w; if(skip) skip = false; else if(r == Runezwj) skip = true; else if(r == Runevs16 && lastr != Runeerror){ clw += sl_wcwidth_vs16(lastr); lastr = Runeerror; }else if(sl_likely((w = sl_wcwidth(r)) > 0)){ lastr = r; clw += w; }else if(w < 0) return n; } if(incluster && totw + clw <= maxwidth) cnt = n; return cnt; } // 'weak' means we don't need to accurately reproduce the type, so // for example #s32(0) can be printed as just 0. this is used // printing in a context where a type is already implied, e.g. inside // an array. sl_nonnull(1) sl_rd(1, 2) static void cvalue_printdata(const void *data, usize len, sl_v type, int nt, bool weak) { if(type == sl_runesym){ rune_print(*(Rune*)data); return; } if(type == sl_utf8sym && sl.pr.princ){ outc(*(u8int*)data); return; } int n = 0; if(iscons(type)){ if(car_(type) != sl_arrsym) abort(); usize i; sl_v eltype = car(cdr_(type)); usize cnt, elsize; if(iscons(cdr_(cdr_(type)))){ cnt = tosize(car_(cdr_(cdr_(type)))); elsize = cnt ? len/cnt : 0; }else{ // incomplete array type elsize = ctype_sizeof(eltype); cnt = elsize ? len/elsize : 0; } if(eltype == sl_utf8sym){ usize sn = utf8_len4width(data, len); if(sl.pr.princ){ outsn(data, sn); if(sn < len) outsc("..."); }else{ outc('"'); print_str((char*)data, sn); if(sn < len) outsc("..."); outc('"'); } return; } if(eltype == sl_runesym && sl.pr.princ){ char buf[UTFmax+1]; usize sn = runes_cnt4width(data, cnt, elsize); for(i = 0; i < sn; i++){ n = runetochar(buf, (Rune*)((char*)data + i*elsize)); buf[n] = 0; outsn(buf, n); } if(sn < cnt) outsc("..."); return; } if(!weak){ if(eltype == sl_u8sym){ outsc("#vu8("); }else{ outsc("#arr("); sl_print_child(eltype); if(cnt > 0) outc(' '); } }else{ outs("#("); } for(i = 0; i < cnt; i++){ if(i > 0) outc(' '); if(sl.pr.length >= 0 && i >= (usize)sl.pr.length){ outs("..."); break; } cvalue_printdata(data, elsize, eltype, sym_numtype(eltype), true); data = (char*)data + elsize; } outc(')'); return; } if(!valid_numtype(nt)) abort(); bool withtype = !weak && !sl.pr.princ && nt != T_F64 && nt != T_BIG; if(withtype){ outc('#'); outs(sym_name(type)); outc('('); } if(nt == T_F32 || nt == T_F64){ char buf[64]; double d; int ndec; if(type == sl_f32sym){ d = *(float*)data; ndec = 8; }else{ d = *(double*)data; ndec = 16; } if(!isfinite(d)){ outc(signbit(d) ? '-' : '+'); if(isinf(d)) outsc("inf.0"); else if(isnan(d)) outsc("nan.0"); else abort(); }else if(d == 0){ if(signbit(d)) outc('-'); outsc("0.0"); }else{ snprint_real(buf, sizeof(buf), d, 0, ndec, 3, 10); bool hasdec = strpbrk(buf, ".eE") != nil; outs(buf); if(!hasdec) outsc(".0"); } }else{ u64int x = 0; int base; bool radix, sign = false; switch(nt){ case T_P32: x = *(u32int*)data; base = sl.pr.ptr_base; radix = sl.pr.ptr_radix; break; case T_P64: x = *(u64int*)data; base = sl.pr.ptr_base; radix = sl.pr.ptr_radix; break; default: base = sl.pr.base; radix = sl.pr.radix; if(nt == T_BIG){ if(base != 10 && (base & (base-1)) != 0) cthrow(error(sl_errarg, "unsupported print base for bignums: %d", base), data); break; } if(is_nt_uint(nt)) x = conv_to_u64(sl_nil, data, nt); else{ s64int sx = conv_to_s64(sl_nil, data, nt); if(sx >= 0) x = sx; else{ x = -sx; sign = true; } } break; } // this combination makes no sense - it's impossible to read it back correctly if(withtype && !radix && base != 10) cthrow(error(sl_errarg, "print: non-fixnum, non-default base and radix disabled"), data); if((n = print_radix_prefix(radix, base)) < 0) cthrow(error(sl_errio, "write failed"), data); sl.pr.x += n; if(nt == T_BIG){ mpint *i = *(mpint**)data; char *s = mptoa(i, base, nil, 0); if(s == nil) cthrow(error(sl_errmem, "no memory for a bignum printout"), data); n = ios_write(sl.pr.f, s, strlen(s)); MEM_FREE(s); }else{ if(sign) outc('-'); n = print_num(x, base); } } if(n < 0) cthrow(error(sl_errio, "write failed"), data); sl.pr.x += n; if(withtype) outc(')'); } static void unboxed_print(sl_v a) { if(sl_isrune(a)){ rune_print(torune(a)); return; } assert(isubnum(a)); sl_numtype nt = ubnumtype(a); sl_v typesym = unboxedtypesyms[nt]; sl_fx v = ubnumval(a); #if !defined(BITS64) if(nt > T_P32) nt -= T_P64-T_P32; #endif cvalue_printdata(ubnumptr(v, nt), unboxedtypes[nt]->size, typesym, nt, false); } static void cvalue_print(sl_v v) { sl_cv *cv = ptr(sl_cv, v); void *data = cvalue_data(v); sl_v label; if(cv_class(cv) == sl_builtintype){ label = (sl_v)ptrhash_get(slvm.reverse_dlsym_lookup, cv); assert(label != (sl_v)HT_NOTFOUND); if(sl.pr.princ){ outs(sym_name(label)); }else{ outsc("#fn("); outs(sym_name(label)); outc(')'); } }else if(cv_class(cv)->vtable != nil && cv_class(cv)->vtable->print != nil){ cv_class(cv)->vtable->print(v); }else{ sl_v type = cv_type(cv); usize len = cv_len(cv); cvalue_printdata(data, len, type, sym_numtype(type), false); } } static sl_fx getbase(sl_v sym) { sl_v v = sym_value(sym); if(sl_unlikely(!sl_isnum(v))) cthrow(type_error(sym_name(sym), "num", v), sym); sl_fx base = tooffset(v); if(sl_unlikely(base < 2 || base > 36)) cthrow(error(sl_errarg, "unsupported print base: %d", (int)base), sym); return base; } void sl_print(sl_v v) { sl.pr.circle = sym_value(circlesym) != sl_nil; sl.pr.pretty = sym_value(sl_printprettysym) != sl_nil; sl.pr.princ = sym_value(sl_printreadablysym) == sl_nil; sl.pr.syntax_sugar = sym_value(syntaxsugarsym) != sl_nil; sl_v pl; if(sl.pr.pretty && sl_isnum(pl = sym_value(rmarginsym))) sl.pr.rmargin = tooffset(pl); else sl.pr.rmargin = -1; pl = sym_value(lengthsym); sl.pr.length = sl_isnum(pl) ? tooffset(pl) : -1; pl = sym_value(levelsym); sl.pr.level = sl_isnum(pl) ? tooffset(pl) : -1; pl = sym_value(widthsym); sl.pr.width = sl_isnum(pl) ? tooffset(pl) : -1; sl.pr.p_level = 0; sl.pr.fnname = false; sl.pr.base = getbase(basesym); sl.pr.radix = sym_value(radixsym) != sl_nil; sl.pr.ptr_base = getbase(ptrbasesym); sl.pr.ptr_radix = sym_value(ptrradixsym) != sl_nil; sl.pr.label = 0; sl.pr.x = sl.pr.y = 0; if(sl.pr.circle) sl_print_traverse(v); sl_print_child(v); if(iscons(v) || isvec(v) || isfn(v) || (iscvalue(v) && !isstr(v))){ htable_reset(&sl.pr.conses, HT_N_INLINE); if(sl.pr.circle) memset(sl.consflags, 0, sizeof(u32int)*bitvector_nwords(slvm.heapsize/sizeof(sl_aligned))); } } void sl_print_init(void) { set(sl_printprettysym = mk_csym("*print-pretty*"), sl_t); set(sl_printreadablysym = mk_csym("*print-readably*"), sl_t); set(circlesym = mk_csym("*print-circle*"), sl_nil); set(rmarginsym = mk_csym("*print-right-margin*"), fixnum(100)); set(lengthsym = mk_csym("*print-length*"), sl_nil); set(levelsym = mk_csym("*print-level*"), sl_nil); set(widthsym = mk_csym("*print-width*"), sl_nil); set(radixsym = mk_csym("*print-radix*"), sl_nil); set(basesym = mk_csym("*print-base*"), fixnum(10)); set(ptrradixsym = mk_csym("*print-ptr-radix*"), sl_t); set(ptrbasesym = mk_csym("*print-ptr-base*"), fixnum(16)); set(syntaxsugarsym = mk_csym("*print-syntax-sugar*"), sl_t); }