Historical internal name "uncommonlisp" retired in favor of the
public name "lumbda" ahead of lumbda.com going live. Scope of
this commit:
Source files renamed:
uncommonlisp.py -> lumbda.py
asm/uncommonlisp.s -> asm/lumbda.s
c/uncommonlisp.h -> c/lumbda.h
whitepaper/uncommonlisp-whitepaper -> whitepaper/lumbda-whitepaper (.rst + .pdf)
Binaries renamed (tracked ones; c/ was always gitignored):
asm/uncommonlisp, asm/uncommonlisp-gc, asm/uncommonlisp.o,
asm/uncommonlisp-gc.o -> asm/lumbda(-gc)(.o)
c/.gitignore -> ignores lumbda
Internal string updates (sed pass ordered longest-first):
asm/uncommonlisp -> asm/lumbda
c/uncommonlisp -> c/lumbda
uncommonlisp.py -> lumbda.py
UNCOMMONLISP_BIN -> LUMBDA_BIN (asm/test.sh env var)
"uncommonlisp> " -> "lumbda> " (asm REPL prompt baked into binary)
UNCOMMONLISP -> LUMBDA (macros, comments)
uncommonlisp -> lumbda (prose)
Binary portal magic updated:
"ULPORTAL" -> "LUMBDAB1" # "Lumbda Binary v1"
Old portal files are not backward-compatible — this is a deliberate
break since it's the rename moment. S-expression portals already
carry their own ";; lumbda-portal v1" header and remain cleanly
versioned.
WHITEPAPER.pdf / WHITEPAPER.rst symlinks repointed to the renamed
files. Makefile's whitepaper target targets lumbda-whitepaper.pdf.
Not changed (intentional, separate phases):
- Filesystem directory /home/fox/git/uncommonlisp itself
(fox renames locally and the gitlab repo URL in a follow-up)
- tests.py hardcoded cwd=/home/fox/git/uncommonlisp
(matches the current on-disk location; will flip when the
directory rename ships)
- Git history (immutable; old commits still say uncommonlisp,
which is correct — that's what they were)
Verified:
137 asm no-GC + 137 asm GC + 571 Python + 83 C + 189 shared
functional tests all pass under the new names.
bench-gc-http (2000 req): all 4 cells behave as expected
(cells 1/2 flat, 3 leaks, 4 bounded at 1 chunk).
Python REPL, C REPL, asm REPL all start cleanly.
260 lines
6.6 KiB
C
260 lines
6.6 KiB
C
/*
|
|
* printer.c — show/display/write for Scheme values
|
|
*/
|
|
#include "lumbda.h"
|
|
|
|
/* Dynamic string builder */
|
|
typedef struct {
|
|
char *buf;
|
|
size_t len;
|
|
size_t cap;
|
|
} StringBuilder;
|
|
|
|
static void sb_init(StringBuilder *sb) {
|
|
sb->cap = 128;
|
|
sb->buf = (char *)ul_malloc(sb->cap);
|
|
sb->buf[0] = '\0';
|
|
sb->len = 0;
|
|
}
|
|
|
|
static void sb_append(StringBuilder *sb, const char *s, size_t n) {
|
|
while (sb->len + n + 1 > sb->cap) {
|
|
sb->cap *= 2;
|
|
sb->buf = (char *)ul_realloc(sb->buf, sb->cap);
|
|
}
|
|
memcpy(sb->buf + sb->len, s, n);
|
|
sb->len += n;
|
|
sb->buf[sb->len] = '\0';
|
|
}
|
|
|
|
static void sb_appendz(StringBuilder *sb, const char *s) {
|
|
sb_append(sb, s, strlen(s));
|
|
}
|
|
|
|
static void sb_appendc(StringBuilder *sb, char c) {
|
|
sb_append(sb, &c, 1);
|
|
}
|
|
|
|
static void show_value(Value v, bool display, StringBuilder *sb);
|
|
|
|
static void show_pair(Value v, bool display, StringBuilder *sb) {
|
|
sb_appendc(sb, '(');
|
|
Value n = v;
|
|
bool first = true;
|
|
while (IS_PAIR(n)) {
|
|
if (!first) sb_appendc(sb, ' ');
|
|
first = false;
|
|
show_value(CAR(n), display, sb);
|
|
n = CDR(n);
|
|
}
|
|
if (!IS_NIL(n)) {
|
|
sb_appendz(sb, " . ");
|
|
show_value(n, display, sb);
|
|
}
|
|
sb_appendc(sb, ')');
|
|
}
|
|
|
|
static void show_escaped_string(const char *s, size_t len, StringBuilder *sb) {
|
|
sb_appendc(sb, '"');
|
|
for (size_t i = 0; i < len; i++) {
|
|
switch (s[i]) {
|
|
case '\\': sb_appendz(sb, "\\\\"); break;
|
|
case '"': sb_appendz(sb, "\\\""); break;
|
|
case '\n': sb_appendz(sb, "\\n"); break;
|
|
case '\t': sb_appendz(sb, "\\t"); break;
|
|
case '\r': sb_appendz(sb, "\\r"); break;
|
|
default: sb_appendc(sb, s[i]); break;
|
|
}
|
|
}
|
|
sb_appendc(sb, '"');
|
|
}
|
|
|
|
static void show_value(Value v, bool display, StringBuilder *sb) {
|
|
if (IS_NIL(v)) { sb_appendz(sb, "()"); return; }
|
|
if (IS_VOID(v)) { return; } /* void prints nothing */
|
|
if (IS_TRUE(v)) { sb_appendz(sb, "#t"); return; }
|
|
if (IS_FALSE(v)){ sb_appendz(sb, "#f"); return; }
|
|
if (IS_EOF(v)) { sb_appendz(sb, "#<eof>"); return; }
|
|
|
|
if (IS_CHAR(v)) {
|
|
int c = AS_CHAR(v);
|
|
if (display) {
|
|
sb_appendc(sb, (char)c);
|
|
} else {
|
|
sb_appendz(sb, "#\\");
|
|
switch (c) {
|
|
case ' ': sb_appendz(sb, "space"); break;
|
|
case '\n': sb_appendz(sb, "newline"); break;
|
|
case '\t': sb_appendz(sb, "tab"); break;
|
|
case '\r': sb_appendz(sb, "return"); break;
|
|
case '\0': sb_appendz(sb, "null"); break;
|
|
case '\x1b': sb_appendz(sb, "escape"); break;
|
|
default: sb_appendc(sb, (char)c); break;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (IS_INT(v)) {
|
|
char buf[32];
|
|
snprintf(buf, sizeof(buf), "%lld", (long long)as_int(v));
|
|
sb_appendz(sb, buf);
|
|
return;
|
|
}
|
|
|
|
if (IS_DOUBLE(v)) {
|
|
double d = as_double(v);
|
|
if (isinf(d)) {
|
|
sb_appendz(sb, d > 0 ? "+inf.0" : "-inf.0");
|
|
return;
|
|
}
|
|
if (isnan(d)) {
|
|
sb_appendz(sb, "+nan.0");
|
|
return;
|
|
}
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "%.17g", d);
|
|
/* Ensure a decimal point for Scheme compat */
|
|
if (!strchr(buf, '.') && !strchr(buf, 'e') && !strchr(buf, 'n') && !strchr(buf, 'i')) {
|
|
size_t n = strlen(buf);
|
|
buf[n] = '.'; buf[n+1] = '0'; buf[n+2] = '\0';
|
|
}
|
|
sb_appendz(sb, buf);
|
|
return;
|
|
}
|
|
|
|
if (IS_SYM(v)) {
|
|
sb_appendz(sb, sym_name(v));
|
|
return;
|
|
}
|
|
|
|
if (IS_RATIONAL(v)) {
|
|
Rational *r = AS_RATIONAL(v);
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "%lld/%lld", (long long)r->num, (long long)r->den);
|
|
sb_appendz(sb, buf);
|
|
return;
|
|
}
|
|
|
|
if (IS_BUILTIN(v)) {
|
|
sb_appendz(sb, "#<builtin>");
|
|
return;
|
|
}
|
|
|
|
if (!IS_PTR(v)) {
|
|
char buf[32];
|
|
snprintf(buf, sizeof(buf), "#<unknown:%llx>", (unsigned long long)v);
|
|
sb_appendz(sb, buf);
|
|
return;
|
|
}
|
|
|
|
/* Heap objects */
|
|
ObjType type = obj_type(v);
|
|
|
|
switch (type) {
|
|
case OBJ_PAIR:
|
|
show_pair(v, display, sb);
|
|
break;
|
|
|
|
case OBJ_STRING:
|
|
case OBJ_MUTABLE_STRING: {
|
|
ULString *s = AS_STRING(v);
|
|
if (display) {
|
|
sb_append(sb, s->data, s->len);
|
|
} else {
|
|
show_escaped_string(s->data, s->len, sb);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case OBJ_VECTOR: {
|
|
ULVector *vec = AS_VECTOR(v);
|
|
sb_appendz(sb, "#(");
|
|
for (size_t i = 0; i < vec->len; i++) {
|
|
if (i > 0) sb_appendc(sb, ' ');
|
|
show_value(vec->data[i], display, sb);
|
|
}
|
|
sb_appendc(sb, ')');
|
|
break;
|
|
}
|
|
|
|
case OBJ_HASHTABLE:
|
|
sb_appendz(sb, "#<hash-table>");
|
|
break;
|
|
|
|
case OBJ_PROC: {
|
|
Proc *p = AS_PROC(v);
|
|
char buf[128];
|
|
snprintf(buf, sizeof(buf), "#<procedure %s>", p->name ? p->name : "λ");
|
|
sb_appendz(sb, buf);
|
|
break;
|
|
}
|
|
|
|
case OBJ_COMPILED_PROC: {
|
|
CompiledProc *cp = AS_COMPILED_PROC(v);
|
|
char buf[128];
|
|
snprintf(buf, sizeof(buf), "#<compiled %s>", cp->name ? cp->name : "λ");
|
|
sb_appendz(sb, buf);
|
|
break;
|
|
}
|
|
|
|
case OBJ_MACRO:
|
|
sb_appendz(sb, "#<macro>");
|
|
break;
|
|
|
|
case OBJ_CONTINUATION:
|
|
sb_appendz(sb, "#<continuation>");
|
|
break;
|
|
|
|
case OBJ_PORT: {
|
|
ULPort *p = AS_PORT(v);
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "#<%s-%s-port>",
|
|
p->kind == PORT_STRING ? "string" : "file",
|
|
p->dir == PORT_INPUT ? "input" : "output");
|
|
sb_appendz(sb, buf);
|
|
break;
|
|
}
|
|
|
|
case OBJ_ERROR: {
|
|
ErrorObject *e = AS_ERROR(v);
|
|
char buf[256];
|
|
snprintf(buf, sizeof(buf), "#<error-object \"%s\">", e->message);
|
|
sb_appendz(sb, buf);
|
|
break;
|
|
}
|
|
|
|
case OBJ_ENV:
|
|
sb_appendz(sb, "#<environment>");
|
|
break;
|
|
|
|
case OBJ_CODE:
|
|
sb_appendz(sb, "#<code>");
|
|
break;
|
|
|
|
case OBJ_SYNTAX_TRANSFORMER:
|
|
sb_appendz(sb, "#<syntax-transformer>");
|
|
break;
|
|
|
|
case OBJ_RATIONAL: {
|
|
Rational *r = AS_RATIONAL(v);
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "%lld/%lld", (long long)r->num, (long long)r->den);
|
|
sb_appendz(sb, buf);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
char *show(Value v, bool display) {
|
|
StringBuilder sb;
|
|
sb_init(&sb);
|
|
show_value(v, display, &sb);
|
|
return sb.buf;
|
|
}
|
|
|
|
void print_value(Value v, bool display, FILE *out) {
|
|
char *s = show(v, display);
|
|
fputs(s, out);
|
|
ul_free(s);
|
|
}
|