#include "sl.h"#include "operators.h"#include "cvalues.h"#include "ieee754.h"#include "print.h"#include "read.h" #define LOG2_10 3.321928094887362347870319429489 sl_v sl_printreadablysym, sl_printprettysym;static sl_v widthsym, circlesym;static sl_v lengthsym, levelsym;static sl_v radixsym, basesym;static sl_v ptrradixsym, ptrbasesym; static inline voidoutc(ios *f, char c){ if(ios_putc(f, c) != 1) lerrorf(sl_errio, "write failed"); if(c != '\n') sl.hpos += sl_wcwidth(c); else sl.hpos = 0;} static inline voidoutsn(ios *f, const char *s, usize n){ if(ios_write(f, s, n) != n) lerrorf(sl_errio, "write failed"); const char *nl = llt_memrchr(s, '\n', n); bool err; if(nl == nil) sl.hpos += u8_strwidth(s, n, &err); else if((nl+1-s) < (intptr)n) sl.hpos = u8_strwidth(nl+1, s+n-nl-1, &err);} static inline voidouts(ios *f, const char *s){ outsn(f, s, strlen(s));} #define outsc(f, s) do{ \ const char vs[] = s; \ outsn(f, vs, sizeof(vs)-1); \}while(0) static intoutindent(ios *f, int n){ // move back to left margin if we get too indented if(n > sl.scr_width-12) n = 2; int n0 = n; if(ios_putc(f, '\n') != 1) goto err; sl.vpos++; sl.hpos = n; while(n){ if(ios_putc(f, ' ') != 1) goto err; n--; } return n0;err: lerrorf(sl_errio, "write failed");} static intprint_radix_prefix(ios *f, bool radix, int base){ if(!radix) return 0; switch(base){ case 2: outsc(f, "#b"); return 0; case 8: outsc(f, "#o"); return 0; case 10: outsc(f, "#d"); return 0; case 16: outsc(f, "#x"); return 0; default: return ios_printf(f, "#%dr", base); }} static intprint_num(ios *f, 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(f, tmp, n) : 0; } char *s = uint2str(tmp, sizeof(tmp), x, base); return ios_write(f, s, tmp+sizeof(tmp)-1-s);} static voidprint_str(ios *f, 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(c == '\\') outsc(f, "\\\\"); else if(c == '"') outsc(f, "\\\""); else if(c >= 32 && c < 0x7f) outc(f, c); else{ outsc(f, "\\x"); outc(f, hexdig[c>>4]); outc(f, hexdig[c&0xf]); } } }else{ while(i < sz){ usize n = u8_escape(buf, sizeof(buf), str, &i, sz, true, false); outsn(f, buf, n-1); } }} voidsl_print_chr(ios *f, char c){ outc(f, c);} voidsl_print_str(ios *f, const char *s, bool princ){ if(princ) outs(f, s); else print_str(f, s, strlen(s));} voidsl_print_traverse(sl_v v){ sl_v *bp; while(iscons(v)){ if(ismarked(v)){ bp = (sl_v*)ptrhash_bp(&sl.printconses, (void*)v); if(*bp == (sl_v)HT_NOTFOUND) *bp = fixnum(sl.printlabel++); 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.printconses, (void*)v); if(*bp == (sl_v)HT_NOTFOUND) *bp = fixnum(sl.printlabel++); return; } 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(v); sl_print_traverse(f->bcode); sl_print_traverse(f->vals); sl_print_traverse(f->env); }else if(iscvalue(v)){ sl_cv *cv = ptr(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); sl_type *t = cv_class(cv); if(t->vtable != nil && t->vtable->print_traverse != nil) t->vtable->print_traverse(v); }} static voidprint_sym_name(ios *f, 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(!symchar(name[i])){ escape = true; if(name[i] == '|' || name[i] == '\\'){ charescape = true; break; } } i++; } if(escape){ if(charescape){ outc(f, '|'); i = 0; while(name[i]){ if(name[i] == '|' || name[i] == '\\') outc(f, '\\'); outc(f, name[i]); i++; } outc(f, '|'); }else{ outc(f, '|'); outs(f, name); outc(f, '|'); } }else{ outs(f, 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.*/#define SMALL_STR_LEN 24static inline inttinyp(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 cv_len(ptr(v)) < SMALL_STR_LEN; if(iscvalue(v)) return valid_numtype(cv_numtype(ptr(v))); return isfixnum(v) || isunboxed(v) || isbuiltin(v) || isspecial(v);} static boolsmallp(sl_v v){ if(tinyp(v)) return true; if(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 intspecialindent(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 intlengthestimate(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(isrune(v)) return 4; return -1;} static intallsmallp(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 intindentafter3(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 intindentafter2(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 boolindentevery(sl_v v){ // indent before every subform of a special form, unless every // subform is "small" sl_v c = car_(v); if(c == sl_lambda || c == sl_setqsym) return false; //if(c == sl.IF) // TODO: others // return !allsmallp(cdr_(v)); return false;} static boolblockindent(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 voidprint_cons(ios *f, sl_v v){ sl_v cd; const char *op; if(iscons(cdr_(v)) && cdr_(cdr_(v)) == sl_nil && !ptrhash_has(&sl.printconses, (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 unmark_cons(v); unmark_cons(cdr_(v)); outs(f, op); sl_print_child(f, car_(cdr_(v))); return; } int startpos = sl.hpos; outc(f, '('); int newindent = sl.hpos; int lastv, n = 0, si, ind, est, nextsmall, thistiny; bool always = false, blk = blockindent(v); if(!blk) always = indentevery(v); sl_v head = car_(v); int after3 = indentafter3(head, v); int after2 = indentafter2(head, v); int n_unindented = 1; while(1){ cd = cdr_(v); if(sl.print_length >= 0 && n >= sl.print_length){ outsc(f, "...)"); break; } lastv = sl.vpos; unmark_cons(v); sl_print_child(f, car_(v)); if(!iscons(cd) || ptrhash_has(&sl.printconses, (void*)cd)){ if(cd != sl_nil){ outsc(f, " . "); sl_print_child(f, cd); } outc(f, ')'); break; } if(!sl.print_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.vpos > lastv || (sl.hpos>sl.scr_width/2 && !nextsmall && !thistiny && n>0)) || (sl.hpos > sl.scr_width-4) || (est != -1 && sl.hpos+est > sl.scr_width-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(f, newindent); n_unindented = 1; }else{ n_unindented++; outc(f, ' '); if(n == 0){ // set indent level after printing head si = specialindent(head); if(si != -1) newindent = startpos + si; else if(!blk) newindent = sl.hpos; } } n++; v = cd; }} static void unboxed_print(ios *f, sl_v v);static void cvalue_print(ios *f, sl_v v);static void rune_print(ios *f, Rune r); static boolprint_circle_prefix(ios *f, sl_v v){ sl_v label; if((label = (sl_v)ptrhash_get(&sl.printconses, (void*)v)) != (sl_v)HT_NOTFOUND){ if(!ismarked(v)){ int n = ios_printf(f, "#%"PRIdPTR"#", (intptr)numval(label)); if(n < 1) lerrorf(sl_errio, "write failed"); sl.hpos += n; return true; } int n = ios_printf(f, "#%"PRIdPTR"=", (intptr)numval(label)); if(n < 1) lerrorf(sl_errio, "write failed"); sl.hpos += n; } if(ismanaged(v)) unmark_cons(v); return false;} voidsl_print_child(ios *f, sl_v v){ const char *name; if(sl.print_level >= 0 && sl.p_level >= sl.print_level && (iscons(v) || isvec(v) || isfn(v))){ outc(f, '#'); return; } sl.p_level++; int n; s64int sx; switch(tag(v)){ case TAG_FIXNUM: n = print_radix_prefix(f, sl.print_radix, sl.print_base); if(n >= 0){ sl.hpos += n; sx = numval(v);Lines 1–500 of 1030. Load more
#include "sl.h" #include "operators.h" #include "cvalues.h" #include "ieee754.h" #include "print.h" #include "read.h" #define LOG2_10 3.321928094887362347870319429489 sl_v sl_printreadablysym, sl_printprettysym; static sl_v widthsym, circlesym; static sl_v lengthsym, levelsym; static sl_v radixsym, basesym; static sl_v ptrradixsym, ptrbasesym; static inline void outc(ios *f, char c) { if(ios_putc(f, c) != 1) lerrorf(sl_errio, "write failed"); if(c != '\n') sl.hpos += sl_wcwidth(c); else sl.hpos = 0; } static inline void outsn(ios *f, const char *s, usize n) { if(ios_write(f, s, n) != n) lerrorf(sl_errio, "write failed"); const char *nl = llt_memrchr(s, '\n', n); bool err; if(nl == nil) sl.hpos += u8_strwidth(s, n, &err); else if((nl+1-s) < (intptr)n) sl.hpos = u8_strwidth(nl+1, s+n-nl-1, &err); } static inline void outs(ios *f, const char *s) { outsn(f, s, strlen(s)); } #define outsc(f, s) do{ \ const char vs[] = s; \ outsn(f, vs, sizeof(vs)-1); \ }while(0) static int outindent(ios *f, int n) { // move back to left margin if we get too indented if(n > sl.scr_width-12) n = 2; int n0 = n; if(ios_putc(f, '\n') != 1) goto err; sl.vpos++; sl.hpos = n; while(n){ if(ios_putc(f, ' ') != 1) goto err; n--; } return n0; err: lerrorf(sl_errio, "write failed"); } static int print_radix_prefix(ios *f, bool radix, int base) { if(!radix) return 0; switch(base){ case 2: outsc(f, "#b"); return 0; case 8: outsc(f, "#o"); return 0; case 10: outsc(f, "#d"); return 0; case 16: outsc(f, "#x"); return 0; default: return ios_printf(f, "#%dr", base); } } static int print_num(ios *f, 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(f, tmp, n) : 0; } char *s = uint2str(tmp, sizeof(tmp), x, base); return ios_write(f, s, tmp+sizeof(tmp)-1-s); } static void print_str(ios *f, 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(c == '\\') outsc(f, "\\\\"); else if(c == '"') outsc(f, "\\\""); else if(c >= 32 && c < 0x7f) outc(f, c); else{ outsc(f, "\\x"); outc(f, hexdig[c>>4]); outc(f, hexdig[c&0xf]); } } }else{ while(i < sz){ usize n = u8_escape(buf, sizeof(buf), str, &i, sz, true, false); outsn(f, buf, n-1); } } } void sl_print_chr(ios *f, char c) { outc(f, c); } void sl_print_str(ios *f, const char *s, bool princ) { if(princ) outs(f, s); else print_str(f, 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.printconses, (void*)v); if(*bp == (sl_v)HT_NOTFOUND) *bp = fixnum(sl.printlabel++); 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.printconses, (void*)v); if(*bp == (sl_v)HT_NOTFOUND) *bp = fixnum(sl.printlabel++); return; } 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(v); sl_print_traverse(f->bcode); sl_print_traverse(f->vals); sl_print_traverse(f->env); }else if(iscvalue(v)){ sl_cv *cv = ptr(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); sl_type *t = cv_class(cv); if(t->vtable != nil && t->vtable->print_traverse != nil) t->vtable->print_traverse(v); } } static void print_sym_name(ios *f, 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(!symchar(name[i])){ escape = true; if(name[i] == '|' || name[i] == '\\'){ charescape = true; break; } } i++; } if(escape){ if(charescape){ outc(f, '|'); i = 0; while(name[i]){ if(name[i] == '|' || name[i] == '\\') outc(f, '\\'); outc(f, name[i]); i++; } outc(f, '|'); }else{ outc(f, '|'); outs(f, name); outc(f, '|'); } }else{ outs(f, 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. */ #define SMALL_STR_LEN 24 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 cv_len(ptr(v)) < SMALL_STR_LEN; if(iscvalue(v)) return valid_numtype(cv_numtype(ptr(v))); return isfixnum(v) || isunboxed(v) || isbuiltin(v) || isspecial(v); } static bool smallp(sl_v v) { if(tinyp(v)) return true; if(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(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(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(ios *f, sl_v v) { sl_v cd; const char *op; if(iscons(cdr_(v)) && cdr_(cdr_(v)) == sl_nil && !ptrhash_has(&sl.printconses, (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 unmark_cons(v); unmark_cons(cdr_(v)); outs(f, op); sl_print_child(f, car_(cdr_(v))); return; } int startpos = sl.hpos; outc(f, '('); int newindent = sl.hpos; int lastv, n = 0, si, ind, est, nextsmall, thistiny; bool always = false, blk = blockindent(v); if(!blk) always = indentevery(v); sl_v head = car_(v); int after3 = indentafter3(head, v); int after2 = indentafter2(head, v); int n_unindented = 1; while(1){ cd = cdr_(v); if(sl.print_length >= 0 && n >= sl.print_length){ outsc(f, "...)"); break; } lastv = sl.vpos; unmark_cons(v); sl_print_child(f, car_(v)); if(!iscons(cd) || ptrhash_has(&sl.printconses, (void*)cd)){ if(cd != sl_nil){ outsc(f, " . "); sl_print_child(f, cd); } outc(f, ')'); break; } if(!sl.print_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.vpos > lastv || (sl.hpos>sl.scr_width/2 && !nextsmall && !thistiny && n>0)) || (sl.hpos > sl.scr_width-4) || (est != -1 && sl.hpos+est > sl.scr_width-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(f, newindent); n_unindented = 1; }else{ n_unindented++; outc(f, ' '); if(n == 0){ // set indent level after printing head si = specialindent(head); if(si != -1) newindent = startpos + si; else if(!blk) newindent = sl.hpos; } } n++; v = cd; } } static void unboxed_print(ios *f, sl_v v); static void cvalue_print(ios *f, sl_v v); static void rune_print(ios *f, Rune r); static bool print_circle_prefix(ios *f, sl_v v) { sl_v label; if((label = (sl_v)ptrhash_get(&sl.printconses, (void*)v)) != (sl_v)HT_NOTFOUND){ if(!ismarked(v)){ int n = ios_printf(f, "#%"PRIdPTR"#", (intptr)numval(label)); if(n < 1) lerrorf(sl_errio, "write failed"); sl.hpos += n; return true; } int n = ios_printf(f, "#%"PRIdPTR"=", (intptr)numval(label)); if(n < 1) lerrorf(sl_errio, "write failed"); sl.hpos += n; } if(ismanaged(v)) unmark_cons(v); return false; } void sl_print_child(ios *f, sl_v v) { const char *name; if(sl.print_level >= 0 && sl.p_level >= sl.print_level && (iscons(v) || isvec(v) || isfn(v))){ outc(f, '#'); return; } sl.p_level++; int n; s64int sx; switch(tag(v)){ case TAG_FIXNUM: n = print_radix_prefix(f, sl.print_radix, sl.print_base); if(n >= 0){ sl.hpos += n; sx = numval(v);