4 Commits

Author SHA1 Message Date
5ce8e7a68e handler-bind and signal, which alter no control flow
spec-conditions.md §1 and §2 and nothing else, because those two are worth
having alone: signal returns Unit whatever it finds, a handler that returns
normally leaves the signalling function to carry on, and with nothing matching
it is a no-op. So none of §6's transfer machinery exists yet and no signature
changed - which is the whole reason to do this step first.

The runtime is a linked list. Establishing a handler is two stores and a push
onto a frame on the establishing function's own stack, and signal with an empty
stack is a null check, which is what §2 asks for. Popping is by frame rather
than by count, so restoring what this one displaced is right even if something
below it left the stack out of step.

A condition's type is a hash of its name and not an index: an index would shift
the moment a struct were added, and every handler a running program had already
pushed would match the wrong type. The condition crosses as a pointer, since a
handler runs while the signalling frame is alive and there is nothing to copy -
but what the clause binds is the condition itself, the pointer being a hidden
parameter and the name a slot loaded from it, so a handler passing c to
something expecting the struct is not handed an address.

A clause is lifted into a function of its own, because a handler runs from
wherever the signal was and cannot be a branch in the function that wrote it.
That gives two refusals, both by the house rule. A handler cannot see the
establishing function's locals - that is a closure with an explicit
environment, so a reference to one is refused for that reason rather than
reported as an unknown name. And return inside a handler-bind body is refused,
since the frames are popped on the way out and an early exit would leave them
pointing into a function that has gone.

Settled in advance for the next step: in a dev build every function is
transfer-transparent, because a cell can hold anything and the honest answer to
what it can call is anything. Same bargain as the indirect call, and it means
redefinition acquires no new refusal class. Still open is whether the
discriminated result is returned by value or through an out-parameter.
2026-09-11 07:27:50 +07:00
2df52e2409 Two reloads from one session, which is the daemon's loop
Everything so far installed one module. The daemon's job is N of them against
one long-lived session, and that is where a registry that hands out fresh
storage per module would show up. So the agent test now takes two: the first
introduces a global the process was never built with, the second only reads it.
1007 rather than 7 is the whole assertion.

Getting there needed stdout to be line buffered, set in flan_rt_init. The C
default when stdout is a file or a pipe is a 4K block, so a program running for
minutes with a REPL attached shows nothing until it exits, and a test driving
one cannot see its progress at all - which is how this was found. One write per
line instead of per 4K.

Also written down: flan reload builds a fresh session from source each time, so
if the program file was edited since the process launched, its idea of the
host's names and memory describes a binary that is not running. That is a limit
of the command, not of sessions. And Session.eval's origin defaults to <eval>,
so the daemon has to pass the editor's real buffer path or errors point at a
file that does not exist.
2026-09-10 21:50:30 +07:00
2f38738f84 Emit wasm 2026-09-10 17:41:06 +07:00
6d86d09a84 Type checking and stuff 2026-09-10 17:27:53 +07:00