Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@
is updated to key off the `:error` car, so the suite stays green.

### Fixed
- **`fe_write()` no longer crashes or loops infinitely on circular structures**
(upstream rxi/fe PR #22). A user at the REPL can construct a circular list
with `setcar`/`setcdr`; the old code followed the cycle forever, causing a
stack overflow or hang. The fix borrows `GCMARKBIT` during traversal to detect
cycles and prints `...` in their place, then immediately clears the marks.
- **Comments terminated by `\r` now parse correctly** (upstream rxi/fe PR #25
partial). Source files with Windows-style `\r\n` line endings had their `\r`
swallowed into the next token after a `;` comment, corrupting the parse. The
comment-skip loop now stops on either `\n` or `\r`.
- **GC save in `fe_open()` now covers the `t` symbol** (upstream rxi/fe PR #25
partial). `fe_savegc` was called after `fe_symbol(ctx, "t")`, so a GC cycle
triggered by that allocation could theoretically collect the freshly created
`t` object before it was stored in `ctx->t`. The save now precedes the
allocation.
- **String host primitives no longer truncate at ~4 KB** (GWP-528).
`string-length`, `string-ref`, `substring`, `string-append`/`str`, and `repr`
copied through a fixed 4 KB C buffer, so any string past ~4095 bytes was
Expand Down Expand Up @@ -94,6 +108,9 @@ First standalone release, split out from the KN-86 emulator.
- `GCSTACKSIZE` is compile-time configurable (default 256). The desktop build
raises it to 8192 so recursive code has headroom; hosts that vendor the
kernel can keep 256.
- `fe_write()` is safe on circular structures (upstream PR #22).
- Comment parser terminates on `\r` as well as `\n` (upstream PR #25).
- `fe_savegc` in `fe_open()` precedes the `t` symbol allocation (upstream PR #25).

### Notes
- `kec build` isn't a compiler — Fe is a tree-walking interpreter. It inlines
Expand Down
30 changes: 23 additions & 7 deletions kernel/fe.c
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ static void writestr(fe_Context *ctx, fe_WriteFn fn, void *udata, const char *s)
while (*s) { fn(ctx, udata, *s++); }
}

void fe_write(fe_Context *ctx, fe_Object *obj, fe_WriteFn fn, void *udata, int qt) {
static void write_(fe_Context *ctx, fe_Object *obj, fe_WriteFn fn, void *udata, int qt) {
char buf[32];

switch (type(obj)) {
Expand All @@ -368,22 +368,26 @@ void fe_write(fe_Context *ctx, fe_Object *obj, fe_WriteFn fn, void *udata, int q
break;

case FE_TPAIR:
if (tag(obj) & GCMARKBIT) { writestr(ctx, fn, udata, "..."); break; }
fn(ctx, udata, '(');
for (;;) {
fe_write(ctx, car(obj), fn, udata, 1);
fe_Object *tmp = car(obj);
tag(obj) |= GCMARKBIT;
write_(ctx, tmp, fn, udata, 1);
obj = cdr(obj);
if (type(obj) != FE_TPAIR) { break; }
fn(ctx, udata, ' ');
if (tag(obj) & GCMARKBIT) { writestr(ctx, fn, udata, "..."); break; }
}
if (!isnil(obj)) {
if (!isnil(obj) && !(tag(obj) & GCMARKBIT)) {
writestr(ctx, fn, udata, " . ");
fe_write(ctx, obj, fn, udata, 1);
write_(ctx, obj, fn, udata, 1);
}
fn(ctx, udata, ')');
break;

case FE_TSYMBOL:
fe_write(ctx, car(cdr(obj)), fn, udata, 0);
write_(ctx, car(cdr(obj)), fn, udata, 0);
break;

case FE_TSTRING:
Expand All @@ -406,6 +410,18 @@ void fe_write(fe_Context *ctx, fe_Object *obj, fe_WriteFn fn, void *udata, int q
}
}

static void unmarkpairs(fe_Object *obj) {
for (; !isnil(obj) && (tag(obj) & GCMARKBIT); obj = cdr(obj)) {
tag(obj) &= ~GCMARKBIT;
unmarkpairs(car(obj));
}
}

void fe_write(fe_Context *ctx, fe_Object *obj, fe_WriteFn fn, void *udata, int qt) {
write_(ctx, obj, fn, udata, qt);
unmarkpairs(obj);
}


static void writefp(fe_Context *ctx, void *udata, char chr) {
unused(ctx);
Expand Down Expand Up @@ -485,7 +501,7 @@ static fe_Object* read_(fe_Context *ctx, fe_ReadFn fn, void *udata) {
return NULL;

case ';':
while (chr && chr != '\n') { chr = fn(ctx, udata); }
while (chr && chr != '\n' && chr != '\r') { chr = fn(ctx, udata); }
return read_(ctx, fn, udata);

case ')':
Expand Down Expand Up @@ -868,11 +884,11 @@ fe_Context* fe_open(void *ptr, int size) {
}

/* init objects */
save = fe_savegc(ctx);
ctx->t = fe_symbol(ctx, "t");
fe_set(ctx, ctx->t, ctx->t);

/* register built in primitives */
save = fe_savegc(ctx);
for (i = 0; i < P_MAX; i++) {
fe_Object *v = object(ctx);
settype(v, FE_TPRIM);
Expand Down
Loading