298 lines
8.4 KiB
Markdown
298 lines
8.4 KiB
Markdown
ENSIFER 0.1.20
|
|||
|
|
============
|
||
|
|
|
||
|
|
A small Model-T-style enfilade in C with an S7 Scheme FFI.
|
||
|
|
|
||
|
|
The point of this package is to make the core mechanism usable without making
|
||
|
|
the reader learn the whole Xanadu/Green vocabulary first.
|
||
|
|
|
||
|
|
Start with [`doc/USAGE`](doc/USAGE) if you are new to the code. Read
|
||
|
|
[`doc/ensifer.7`](doc/ensifer.7) for the conceptual overview,
|
||
|
|
[`doc/enfilade.3`](doc/enfilade.3) for the C API, and
|
||
|
|
[`doc/enfilade-s7.7`](doc/enfilade-s7.7) for Scheme. The tumbler primitive is
|
||
|
|
documented separately in [`doc/tumbler.3`](doc/tumbler.3) and
|
||
|
|
[`doc/tumbler-s7.7`](doc/tumbler-s7.7).
|
||
|
|
|
||
|
|
THE IDEA
|
||
|
|
--------
|
||
|
|
|
||
|
|
An enfilade is a tree indexed by tumbler fields.
|
||
|
|
|
||
|
|
A tumbler is written as a dotted sequence of unsigned integers:
|
||
|
|
|
||
|
|
1.2.3
|
||
|
|
|
||
|
|
Putting:
|
||
|
|
|
||
|
|
1.2.3 -> datum
|
||
|
|
|
||
|
|
creates the path automatically:
|
||
|
|
|
||
|
|
root
|
||
|
|
|
|
||
|
|
+-- 1
|
||
|
|
|
|
||
|
|
+-- 2
|
||
|
|
|
|
||
|
|
+-- 3 -> datum
|
||
|
|
|
||
|
|
A key may have both a datum and a child enfilade. Lookup returns the datum at
|
||
|
|
that key first, then the contents of the child.
|
||
|
|
|
||
|
|
That is the whole basic mechanism.
|
||
|
|
|
||
|
|
QUICK START
|
||
|
|
-----------
|
||
|
|
|
||
|
|
Configure and build the library, S7 module, and bundled REPL:
|
||
|
|
|
||
|
|
./configure
|
||
|
|
make
|
||
|
|
|
||
|
|
Start the REPL; the enfilade functions are initialized automatically:
|
||
|
|
|
||
|
|
./build/bin/logan
|
||
|
|
|
||
|
|
Install under `/usr/local` (or set another prefix with
|
||
|
|
`./configure --prefix=/path`):
|
||
|
|
|
||
|
|
make install
|
||
|
|
|
||
|
|
Stage an install for packaging or inspection with `DESTDIR`:
|
||
|
|
|
||
|
|
make install DESTDIR=/tmp/ensifer-stage
|
||
|
|
|
||
|
|
Run the regression suite with `make check`. `make clean` removes generated
|
||
|
|
build files; `make distclean` also removes configure output.
|
||
|
|
|
||
|
|
To load the shared module into another S7 host:
|
||
|
|
|
||
|
|
(load "./build/lib/enfilade.so" (inlet 'init_func 'init_enfilade_s7))
|
||
|
|
RANGE
|
||
|
|
-----
|
||
|
|
-----
|
||
|
|
|
||
|
|
`enfilade-get-range` is deliberately unchanged from the existing interface.
|
||
|
|
It ranges over the numeric keys at the current enfilade level:
|
||
|
|
|
||
|
|
(enfilade-get-range e 1 3)
|
||
|
|
|
||
|
|
The bounds are inclusive. A negative end means "through the end".
|
||
|
|
|
||
|
|
This is not a dotted-tumbler interval expression.
|
||
|
|
|
||
|
|
TUMBLERS
|
||
|
|
--------
|
||
|
|
|
||
|
|
The C layer now has a small standalone tumbler module.
|
||
|
|
|
||
|
|
The public primitive is:
|
||
|
|
|
||
|
|
#include "tumbler.h"
|
||
|
|
|
||
|
|
It supplies:
|
||
|
|
|
||
|
|
tumbler_parse()
|
||
|
|
tumbler_free()
|
||
|
|
tumbler_to_string()
|
||
|
|
tumbler_compare()
|
||
|
|
tumbler_add()
|
||
|
|
tumbler_subtract_strong()
|
||
|
|
tumbler_subtract_weak()
|
||
|
|
tumbler_subtract()
|
||
|
|
|
||
|
|
The implementation uses `uint64_t` fields. It therefore has finite field
|
||
|
|
width even though the historical Xanadu representation used arbitrary-precision
|
||
|
|
Humbers. This is a deliberate boundary for the small reference build.
|
||
|
|
|
||
|
|
Whole-tumbler comparison follows the infinitesimal-style ordering used by the
|
||
|
|
small tumbler implementations derived from Udanax work. Missing fields are
|
||
|
|
compared as zero, giving the familiar ordering:
|
||
|
|
|
||
|
|
1 < 1.1 < 1.1.1 < 1.2 < 2
|
||
|
|
|
||
|
|
Arithmetic implements Xanadu's non-commutative tumbler addition and the strong,
|
||
|
|
weak, and generalized difference operations. See `doc/tumbler.3` for the
|
||
|
|
rules and examples.
|
||
|
|
|
||
|
|
C DATA LIFETIME
|
||
|
|
---------------
|
||
|
|
|
||
|
|
`enfilade_t` never owns the datum pointer supplied to `put_enfilade()`.
|
||
|
|
|
||
|
|
For ordinary C code:
|
||
|
|
|
||
|
|
int value = 42;
|
||
|
|
enfilade_t *e = create_enfilade();
|
||
|
|
|
||
|
|
put_enfilade(e, "1.2.3", &value);
|
||
|
|
/* use e while value remains alive */
|
||
|
|
|
||
|
|
destroy_enfilade(e);
|
||
|
|
|
||
|
|
/* value may now go out of scope */
|
||
|
|
|
||
|
|
The enfilade frees its own index nodes, never the datum pointed to by an entry.
|
||
|
|
The caller therefore controls datum lifetime.
|
||
|
|
|
||
|
|
For S7, the datum is an S7 object. The c-object mark function walks every
|
||
|
|
stored datum and marks it, so Scheme objects remain reachable through the
|
||
|
|
enfilade and are reclaimed normally after the enfilade itself is released.
|
||
|
|
|
||
|
|
C API
|
||
|
|
-----
|
||
|
|
|
||
|
|
Create and destroy:
|
||
|
|
|
||
|
|
enfilade_t *create_enfilade(void);
|
||
|
|
void destroy_enfilade(enfilade_t *enf);
|
||
|
|
|
||
|
|
Store and remove:
|
||
|
|
|
||
|
|
enf_result_t put_enfilade(enfilade_t *enf,
|
||
|
|
const char *tumbler,
|
||
|
|
void *datum);
|
||
|
|
|
||
|
|
enf_result_t remove_enfilade(enfilade_t *enf,
|
||
|
|
const char *tumbler);
|
||
|
|
|
||
|
|
Lookup:
|
||
|
|
|
||
|
|
void *getXtumbler_enfilade(enfilade_t *enf, const char *tumbler);
|
||
|
|
|
||
|
|
enf_result_t getXtumbler_values_enfilade(enfilade_t *enf,
|
||
|
|
const char *tumbler,
|
||
|
|
void ***items,
|
||
|
|
size_t *count);
|
||
|
|
|
||
|
|
Range:
|
||
|
|
|
||
|
|
enf_result_t getXrange_values_enfilade(enfilade_t *enf,
|
||
|
|
int64_t start,
|
||
|
|
int64_t end,
|
||
|
|
void ***items,
|
||
|
|
size_t *count);
|
||
|
|
|
||
|
|
Traversal:
|
||
|
|
|
||
|
|
void enfilade_visit_data(enfilade_t *enf,
|
||
|
|
enfilade_datum_visitor visitor,
|
||
|
|
void *context);
|
||
|
|
|
||
|
|
The two array-returning functions allocate the array of datum pointers. The
|
||
|
|
caller frees that array with `free()`. The datum pointers are not transferred
|
||
|
|
or freed.
|
||
|
|
|
||
|
|
`remove_enfilade()` removes only the datum at the exact final key. If that key
|
||
|
|
also names a child enfilade, the child remains. Empty intermediate children
|
||
|
|
are pruned.
|
||
|
|
|
||
|
|
S7 FFI
|
||
|
|
------
|
||
|
|
|
||
|
|
The module defines the c-object type `<enfilade>` and these Scheme functions:
|
||
|
|
|
||
|
|
make-enfilade
|
||
|
|
enfilade-put-data
|
||
|
|
enfilade-remove
|
||
|
|
enfilade-get-by-tumbler
|
||
|
|
enfilade-get-range
|
||
|
|
tumbler-compare
|
||
|
|
tumbler-add
|
||
|
|
tumbler-subtract
|
||
|
|
|
||
|
|
Load the shared object with:
|
||
|
|
|
||
|
|
(load "./build/lib/enfilade.so" (inlet 'init_func 'init_enfilade_s7))
|
||
|
|
|
||
|
|
The module does not depend on S7's `s7_repl()` startup path or on `cload.scm`.
|
||
|
|
`build/bin/logan` is a direct small REPL with the module initialized at startup.
|
||
|
|
|
||
|
|
ERRORS
|
||
|
|
------
|
||
|
|
|
||
|
|
Malformed tumbler text and a missing intermediate child are reported by the
|
||
|
|
S7 binding as `TumblerAddressException`.
|
||
|
|
|
||
|
|
A valid tumbler whose final key has no datum or child returns `()`.
|
||
|
|
|
||
|
|
`enfilade-remove` returns `#t` when a datum was removed and `#f` when the exact
|
||
|
|
final datum was not present. Malformed addresses and allocation failures are
|
||
|
|
errors.
|
||
|
|
|
||
|
|
REFERENCE IMPLEMENTATION
|
||
|
|
-------------------------
|
||
|
|
|
||
|
|
The immediate behavioral reference for the tree is:
|
||
|
|
|
||
|
|
https://raw.githubusercontent.com/enkiv2/ds-lib/refs/heads/master/Enfilade.py
|
||
|
|
|
||
|
|
The corresponding core operations are its `putValue`, `getByTumbler`, `get`,
|
||
|
|
and `getRange` behavior.
|
||
|
|
|
||
|
|
The C implementation deliberately keeps `range` as the existing numeric-key
|
||
|
|
range operation. Its ordered merge visits a key shared by a datum and child
|
||
|
|
once; the Python reference can visit that key twice because it combines the two
|
||
|
|
key lists before sorting.
|
||
|
|
|
||
|
|
WHAT THIS IS
|
||
|
|
------------
|
||
|
|
|
||
|
|
This is a small reference enfilade: a useful, inspectable building block for
|
||
|
|
other structures that need a tumbler-addressed index.
|
||
|
|
|
||
|
|
It is not the complete Udanax Green backend. The larger literature adds
|
||
|
|
specialized enfilades and machinery for WIDs, DISPs, rearrangement, versioning,
|
||
|
|
spans, virtual copies, persistent structures, and the Green storage model.
|
||
|
|
Those are layers to build on top of this primitive, not requirements for using
|
||
|
|
this one.
|
||
|
|
|
||
|
|
For a ZigZag cell catalogue, the natural simple pattern is:
|
||
|
|
|
||
|
|
cell ID (tumbler)
|
||
|
|
|
|
||
|
|
v
|
||
|
|
enfilade
|
||
|
|
|
|
||
|
|
v
|
||
|
|
cell pointer
|
||
|
|
|
||
|
|
New cells can therefore be catalogued under newly allocated tumbler IDs without
|
||
|
|
making the catalogue itself understand the cell's internal representation.
|
||
|
|
|
||
|
|
FILES
|
||
|
|
-----
|
||
|
|
|
||
|
|
inc/ public C and S7 headers
|
||
|
|
src/ C implementation, bundled S7, and REPL sources
|
||
|
|
test/ C regression tests and S7 smoke test
|
||
|
|
scheme/ Scheme reference implementation and S7 support scripts
|
||
|
|
doc/ usage guide and groff manual pages
|
||
|
|
configure compiler and shared-library capability checks
|
||
|
|
Makefile build, check, install, uninstall, and clean targets
|
||
|
|
|
||
|
|
TESTS
|
||
|
|
-----
|
||
|
|
|
||
|
|
Run all regression checks with:
|
||
|
|
|
||
|
|
make check
|
||
|
|
|
||
|
|
This builds and runs the pure C tests and the bundled S7 smoke test.
|
||
|
|
|
||
|
|
HISTORICAL REFERENCES
|
||
|
|
---------------------
|
||
|
|
|
||
|
|
* Xanadu Hypertext Documents, especially "Enfilade Theory" and "Tumblers and
|
||
|
|
Humbers":
|
||
|
|
https://sentido-labs.com/en/library/201904240732/Xanadu%20Hypertext%20Documents.html
|
||
|
|
|
||
|
|
* Udanax Green / Xanalogical Structure overview:
|
||
|
|
https://www.mprove.de/visionreality/media/xuDation.html
|
||
|
|
|
||
|
|
* Ted Nelson and Udanax developer archive:
|
||
|
|
https://www.xanadu.com.au/mail/udanax/
|
||
|
|
|
||
|
|
* Jeff Rush's `xanalogica.tumbler`, a Python implementation explicitly derived
|
||
|
|
from Udanax Green's C source:
|
||
|
|
https://pypi.org/project/xanalogica.tumbler/
|