Command line
What it is
A terminal program with the machine inside it, that reads lines and
prints what they answer; the easiest host to start with. It loads its
modules at run time. The repository is o.se.
Get it running
Needs a C compiler, make and git; on a Mac, xcode-select --install
gives you all three.
git clone --recursive https://github.com/asomadev/unix.o.se
cd unix.o.se && make
That builds ./o.se and its modules: the language, a standard library,
UDP and TCP, time, a line editor and the debugger. Inside a clone of the
asoma workspace, the same host is hosts/unix.o.se: cd hosts/unix.o.se
&& make.
You have it when a prompt answers:
./o.se -f o.se.osen/osen.ose
> (21, 2) /o/add
: 23
Leave with ctrl-C. The first time a prompt opens a network port, macOS
asks whether o.se may accept incoming connections: allow it, or nothing
arrives.
First program
The leading colon marks a message with no address: a bare result. Name it, and read it back:
> /x : (21, 2) /o/add, /x
/x -> /_e
: 23
The prompt says where the name went, /_e, the environment, and then
answers. ./o.se -f repl.ose is the same prompt without osen loaded: it
reads the instruction language only.
What it adds
Into the host register, as /o/ verbs a program calls like the
language’s own:
/o/udp/sock/create—() -> a new socket, as an int/o/udp/sock/bind—("host", port) -> a new socket bound to them/o/udp/sendto—(data, "host", port) or (data, "host", port, socket) -> nothing; the data sent as one datagram. Without a socket the process’s own is used; a bundle travels as its bytes, a blob as it is./o/udp/send—(data, socket) -> nothing; the data sent on a connected socket/o/udp/recv—(socket) -> the next datagram, as an element, and /udp/sender/addr : "host". Waits until one arrives./o/udp/receive—(callback list, port) -> nothing; the list runs when a datagram arrives on the port./o/listenis the same with a port and a function, and(port) /o/serveruns each arrival; both come witho.se.udp./o/udp/close—(socket) -> nothing; the socket closed/o/time/now—() -> the time now, as a timetag/o/load—("path") -> nothing; the file loaded: a.osefile as instructions, anything else parsed as osen and run./o/lines/read—("path") -> a bundle of the file's lines, as strings/o/string/fromfile/read—("path") -> the file's contents as a blob, ending in a zero byte/o/quit—() -> nothing; the process ends/o/hook/parse/pre,/o/hook/parse/post—(x) -> x; the prompt runs them before and after parsing a line, and a program rebinds one to intercept./o/machine/run—(machine) -> the machine after running, a copy; the original is untouched./o/machine/stat—(machine) -> the machine, unchanged, having printed what is in it and whether it faulted/o/machine/send—(machine, "host:port") -> nothing; a copy of the machine sent there to resume. The machine stays bound here; what the far end does with an arrival is its own receiver’s choice./o/machine/use/value—(machine, value) -> the machine after continuing with the value in place of the name it could not find. One read; a second read of the same name faults again./o/machine/store/value—(machine, value) -> the machine after binding the name it could not find to the value and continuing. The binding lasts until the frame that faulted returns;/o/machine/bindmakes it outlast that.
A halted machine comes from the prompt: with /!/inner/trap installed, a
line that faults is held as /m, and the four /o/machine/ verbs and the
language’s /o/frames, /o/backtrace, /o/frame, /o/db and
/o/machine/bind take it from there.
And as instructions: the same UDP under /udp/*, /time/now, files and
streams (/read, /readlines, /write, /load, /stdin, /stdout,
/stderr), the shell (/system), a debugger (/debug) and an inner
machine (/inner/*).
And a queue of four slots, /q0 to /q3, that the prompt runs between
lines: ({ body }, ms, "/name") /continue/every starts a loop, one per
name; ("/name") /continue/stop ends it, and () /continue/stop the one
due soonest; (ms) /continue/after inside a body pauses that loop’s
period; () /continue/list shows every slot with its state;
/continue/hold, /continue/now and /continue/release take a loop off
its clock and put it back; ("/name", "host:port") /continue/copy sends
a copy of a loop to another host, where it is filed under its name;
("/name", value, "/key") /continue/bind binds into a running loop. A
slot reads like a register: /q0/name, /q0/every. /queue/stat
prints them all. A loop whose period fails is parked in its slot, and a
fixed copy comes back into it with (machine, "/name", "host:port")
/o/machine/resume.
A line that fails stops at the failing step: what it bound before the
raise stays bound, nothing after it runs, and /o/load of a file stops
at the file’s first error. The same rule holds on every host.
The network, from the prompt
tools/view.sh starts the view in one command: the census, a
bridge to the browser, the page served from web.o.se beside it, and
this prompt as a node with a card of its own. It prints the address to
open. Run again on the same computer, it joins as a second node. By
hand, ("examples/census.osen") /o/load listens for nodes on UDP 19993
and asks the network who is there, (8888) /o/serve makes the prompt a
node, () /node/hello announces it and () /node/goodbye takes it off,
and () /world/open gives the prompt a world: a name a loop here calls
that nothing binds goes out as a line, world: /rgb/pulse 0 150 0 300,
and reads as what ("/blink/red", 200) /world/set gave it.
Three shell scripts in tools/ wrap what the prompt does over TCP:
machine-get.sh <host> <out.osc> fetches a parked machine off a board,
machine-put.sh <host> <machine.osc> [port [loop]] sends one back, into
its loop when the loop is named, and program-put.sh <host> <file.osen>
sends a program as /o/eval/at does. ws-bridge.sh and http-serve.sh
are the bridge and the page server on their own.
And the osen reference as data: the register /_q,
one entry per name, in the machine a typed line runs in. /_q/o/add at
the prompt answers /o/add’s entry, and (/>/_q) /o/get/addresses lists
every name.
The command line also builds and loads o.se.tcp, o.se.serial,
o.se.ws and o.se.http, each with its own /o/ names: SLIP-framed
TCP, the serial cable to a board, the WebSocket bridge to a browser tab,
and a directory served over HTTP so a tab can load the page. The
modules lists them.