init commit
This commit is contained in:
commit
6300e42bb2
28 files changed
+104005
No files matched your search
@@ -0,0 +1,367 @@
|
||||
ENSIFER ENFILADE — BEGINNER'S GUIDE
|
||||
===================================
|
||||
|
||||
This guide assumes you have never worked with an enfilade before.
|
||||
|
||||
The shortest description is:
|
||||
|
||||
A tumbler is an address.
|
||||
An enfilade is the tree that stores things at those addresses.
|
||||
|
||||
For example:
|
||||
|
||||
1.2.3
|
||||
|
||||
is one tumbler address.
|
||||
|
||||
If we store:
|
||||
|
||||
1.2.3 -> 42
|
||||
|
||||
the enfilade creates this path automatically:
|
||||
|
||||
root
|
||||
|
|
||||
+-- 1
|
||||
|
|
||||
+-- 2
|
||||
|
|
||||
+-- 3 -> 42
|
||||
|
||||
You do not construct those intermediate nodes yourself.
|
||||
|
||||
1. BUILD AND RUN IT
|
||||
-------------------
|
||||
|
||||
From the Ensifer directory, configure and build:
|
||||
|
||||
./configure
|
||||
make
|
||||
|
||||
Then start the REPL:
|
||||
|
||||
./build/bin/logan
|
||||
|
||||
To load the module into a different S7 host:
|
||||
|
||||
(load "./build/lib/enfilade.so" (inlet 'init_func 'init_enfilade_s7))
|
||||
|
||||
2. CREATE ONE
|
||||
-------------
|
||||
|
||||
(define e (make-enfilade))
|
||||
|
||||
`e` is now the enfilade.
|
||||
|
||||
3. PUT SOMETHING IN IT
|
||||
----------------------
|
||||
|
||||
(enfilade-put-data e "1.2.3" 42)
|
||||
|
||||
There is now a datum at address `1.2.3`.
|
||||
|
||||
4. GET IT BACK
|
||||
--------------
|
||||
|
||||
(enfilade-get-by-tumbler e "1.2.3")
|
||||
|
||||
Result:
|
||||
|
||||
(42)
|
||||
|
||||
The result is a Scheme list because a tumbler lookup can produce more than one
|
||||
datum.
|
||||
|
||||
5. PUT MORE IN
|
||||
--------------
|
||||
|
||||
(enfilade-put-data e "1.2.4" 43)
|
||||
(enfilade-put-data e "1.3.1" 99)
|
||||
|
||||
Now:
|
||||
|
||||
(enfilade-get-by-tumbler e "1.2.3")
|
||||
; => (42)
|
||||
|
||||
(enfilade-get-by-tumbler e "1.2")
|
||||
; => (42 43)
|
||||
|
||||
The second lookup stops at key `2` inside the `1` branch and returns the data
|
||||
at that key followed by the data beneath its child enfilade.
|
||||
|
||||
6. A KEY MAY HAVE BOTH A DATUM AND CHILDREN
|
||||
--------------------------------------------
|
||||
|
||||
This is important.
|
||||
|
||||
(enfilade-put-data e "1.2" 99)
|
||||
(enfilade-put-data e "1.2.5" 50)
|
||||
|
||||
Now key `1.2` has both:
|
||||
|
||||
datum = 99
|
||||
child = 5 -> 50
|
||||
|
||||
So:
|
||||
|
||||
(enfilade-get-by-tumbler e "1.2")
|
||||
; => (99 42 43 50)
|
||||
|
||||
The datum at the exact key comes first. The child is then flattened in numeric
|
||||
key order.
|
||||
|
||||
7. REPLACE A DATUM
|
||||
------------------
|
||||
|
||||
A final key contains one datum. Putting another datum at the same final key
|
||||
replaces the old one:
|
||||
|
||||
(enfilade-put-data e "1.2.3" 500)
|
||||
(enfilade-get-by-tumbler e "1.2.3")
|
||||
; => (500)
|
||||
|
||||
8. REMOVE A DATUM
|
||||
-----------------
|
||||
|
||||
(enfilade-remove e "1.2.3")
|
||||
; => #t
|
||||
|
||||
That removes only the datum stored exactly at `1.2.3`.
|
||||
|
||||
It does not delete an unrelated child at that key.
|
||||
|
||||
Removing a nonexistent final datum returns `#f`:
|
||||
|
||||
(enfilade-remove e "1.2.3")
|
||||
; => #f
|
||||
|
||||
If an intermediate child does not exist, the operation reports a
|
||||
`TumblerAddressException` instead.
|
||||
|
||||
9. RANGE
|
||||
--------
|
||||
|
||||
Keep this distinction in mind:
|
||||
|
||||
tumbler lookup = dotted address
|
||||
range = numeric keys at one enfilade level
|
||||
|
||||
For example:
|
||||
|
||||
(enfilade-get-range e 1 3)
|
||||
|
||||
means “visit numeric keys 1, 2, and 3 at this level, inclusive, and flatten
|
||||
their contents.”
|
||||
|
||||
A negative end means “through the end”:
|
||||
|
||||
(enfilade-get-range e 2 -1)
|
||||
|
||||
The `range` interface is intentionally kept exactly as the existing API.
|
||||
|
||||
10. TUMBLERS ARE MORE THAN STRINGS
|
||||
-----------------------------------
|
||||
|
||||
The external notation is a string:
|
||||
|
||||
"1.2.3"
|
||||
|
||||
but the package now has a real C tumbler primitive underneath it.
|
||||
|
||||
The S7 side exposes the useful basic operations too.
|
||||
|
||||
Compare:
|
||||
|
||||
(tumbler-compare "1" "1.1")
|
||||
; => -1
|
||||
|
||||
(tumbler-compare "1.1" "1.1.1")
|
||||
; => -1
|
||||
|
||||
(tumbler-compare "1.2" "1.1.9")
|
||||
; => 1
|
||||
|
||||
Add:
|
||||
|
||||
(tumbler-add "3" "2.16.3")
|
||||
; => "5.16.3"
|
||||
|
||||
(tumbler-add "25.6.46.93" "0.0.3.1.21")
|
||||
; => "25.6.49.1.21"
|
||||
|
||||
Subtract / difference:
|
||||
|
||||
(tumbler-subtract "1.4.3" "1.1.1")
|
||||
; => "0.3.3"
|
||||
|
||||
These are the historical tumbler operations, not ordinary decimal-number
|
||||
addition and subtraction.
|
||||
|
||||
11. WHY THIS MATTERS FOR ZIGZAG
|
||||
-------------------------------
|
||||
|
||||
An enfilade does not need to understand what a datum means.
|
||||
|
||||
That is useful when the datum is a ZigZag cell pointer.
|
||||
|
||||
Conceptually:
|
||||
|
||||
cell ID
|
||||
|
|
||||
v
|
||||
tumbler
|
||||
|
|
||||
v
|
||||
enfilade
|
||||
|
|
||||
v
|
||||
cell
|
||||
|
||||
The catalogue can therefore answer:
|
||||
|
||||
“What cell is stored at this ID?”
|
||||
|
||||
without embedding ZigZag cell structure into the indexing mechanism.
|
||||
|
||||
12. THE C VERSION
|
||||
-----------------
|
||||
|
||||
Include:
|
||||
|
||||
#include "enfilade.h"
|
||||
|
||||
Create:
|
||||
|
||||
enfilade_t *e = create_enfilade();
|
||||
|
||||
Store a pointer:
|
||||
|
||||
int value = 42;
|
||||
put_enfilade(e, "1.2.3", &value);
|
||||
|
||||
Use it while the pointed-to object is alive:
|
||||
|
||||
int *found = getXtumbler_enfilade(e, "1.2.3");
|
||||
|
||||
printf("%d\n", *found);
|
||||
|
||||
Destroy the index before destroying the object it points to:
|
||||
|
||||
destroy_enfilade(e);
|
||||
|
||||
The enfilade never frees `value`.
|
||||
|
||||
The important rule is therefore:
|
||||
|
||||
the enfilade owns its tree;
|
||||
the caller owns its datum.
|
||||
|
||||
13. TUMBLER C API
|
||||
-----------------
|
||||
|
||||
Include:
|
||||
|
||||
#include "tumbler.h"
|
||||
|
||||
Parse a tumbler string:
|
||||
|
||||
tumbler_t t = {0};
|
||||
tumbler_result_t r = tumbler_parse("1.2.3", &t);
|
||||
|
||||
Use it:
|
||||
|
||||
int cmp = tumbler_compare(&t, &t);
|
||||
|
||||
Release it:
|
||||
|
||||
tumbler_free(&t);
|
||||
|
||||
Arithmetic produces another heap-owned tumbler whose storage the caller must
|
||||
also release:
|
||||
|
||||
tumbler_t a = {0}, b = {0}, out = {0};
|
||||
|
||||
tumbler_parse("25.6.46.93", &a);
|
||||
tumbler_parse("0.0.3.1.21", &b);
|
||||
tumbler_add(&a, &b, &out);
|
||||
|
||||
tumbler_free(&a);
|
||||
tumbler_free(&b);
|
||||
tumbler_free(&out);
|
||||
|
||||
14. VALID TUMBLERS
|
||||
------------------
|
||||
|
||||
The current C representation accepts one or more unsigned decimal fields:
|
||||
|
||||
1
|
||||
1.2
|
||||
1.2.3
|
||||
0
|
||||
1.0.3
|
||||
100.0.7
|
||||
|
||||
Malformed examples:
|
||||
|
||||
.1
|
||||
1.
|
||||
1..2
|
||||
1.a.2
|
||||
-1.2
|
||||
|
||||
The zero field is legal. Historical Xanadu addressing uses zero fields as
|
||||
delimiters in some larger address formats; this small enfilade does not assign
|
||||
zero any additional structural meaning.
|
||||
|
||||
15. ERRORS
|
||||
----------
|
||||
|
||||
Malformed tumbler text:
|
||||
|
||||
TumblerAddressException
|
||||
|
||||
Missing intermediate child during lookup/removal:
|
||||
|
||||
TumblerAddressException
|
||||
|
||||
Valid final key with no datum or descendants:
|
||||
|
||||
()
|
||||
|
||||
Allocation failure:
|
||||
|
||||
memory-error
|
||||
|
||||
16. WHAT YOU DO NOT NEED TO LEARN YET
|
||||
--------------------------------------
|
||||
|
||||
You do not need CRUM, POOM, Granfilade, Spanfilade, WID, DISP, orgl, loaf, or
|
||||
Green's virtual-memory machinery to use this object.
|
||||
|
||||
Those names describe the larger historical system and specialized data
|
||||
structures built around enfilades. They are important when implementing those
|
||||
higher layers, but they are not prerequisites for understanding this small
|
||||
primitive.
|
||||
|
||||
The right mental model for this package is simply:
|
||||
|
||||
tumbler -> path through an enfilade -> datum(s)
|
||||
|
||||
17. REFERENCES
|
||||
--------------
|
||||
|
||||
Immediate behavioral reference:
|
||||
|
||||
https://raw.githubusercontent.com/enkiv2/ds-lib/refs/heads/master/Enfilade.py
|
||||
|
||||
Historical Xanadu technical material:
|
||||
|
||||
https://sentido-labs.com/en/library/201904240732/Xanadu%20Hypertext%20Documents.html
|
||||
|
||||
Udanax Green / Xanalogical Structure:
|
||||
|
||||
https://www.mprove.de/visionreality/media/xuDation.html
|
||||
|
||||
Jeff Rush's tumbler implementation:
|
||||
|
||||
https://pypi.org/project/xanalogica.tumbler/
|
||||
@@ -0,0 +1,128 @@
|
||||
.Dd September 24, 2026
|
||||
.Dt ENFILADE-S7 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm enfilade-s7
|
||||
.Nd S7 Scheme bindings for the small enfilade
|
||||
.Sh SYNOPSIS
|
||||
.Bd -literal -offset indent
|
||||
(load "./build/lib/enfilade.so" (inlet 'init_func 'init_enfilade_s7))
|
||||
|
||||
(define e (make-enfilade))
|
||||
(enfilade-put-data e "1.2.3" 42)
|
||||
(enfilade-get-by-tumbler e "1.2.3")
|
||||
.Ed
|
||||
.Sh DESCRIPTION
|
||||
The module wraps the C
|
||||
.Vt enfilade_t
|
||||
in an S7 c-object of type
|
||||
.Ql <enfilade> .
|
||||
The Scheme datum passed to
|
||||
.Fn enfilade-put-data
|
||||
is retained in the C tree as an S7 pointer.
|
||||
The c-object mark callback marks every stored datum during garbage collection.
|
||||
.Sh LOADING
|
||||
Configure and build the S7 module:
|
||||
.Bd -literal -offset indent
|
||||
./configure
|
||||
make
|
||||
.Ed
|
||||
.Pp
|
||||
Load it into an S7 host with:
|
||||
.Bd -literal -offset indent
|
||||
(load "./build/lib/enfilade.so" (inlet 'init_func 'init_enfilade_s7))
|
||||
.Ed
|
||||
.Sh FUNCTIONS
|
||||
.Bl -tag -width enfilade-get-by-tumbler
|
||||
.It Fn make-enfilade
|
||||
Returns a new
|
||||
.Ql <enfilade>
|
||||
object.
|
||||
.It Fn enfilade-put-data enfilade tumbler datum
|
||||
Stores
|
||||
.Fa datum
|
||||
at
|
||||
.Fa tumbler .
|
||||
The tumbler is a string of unsigned decimal fields separated by periods.
|
||||
Missing intermediate child enfilades are created automatically.
|
||||
The return value is unspecified.
|
||||
.It Fn enfilade-remove enfilade tumbler
|
||||
Removes the datum at the exact final key.
|
||||
Returns
|
||||
.Ql #t
|
||||
when a datum was removed and
|
||||
.Ql #f
|
||||
when the exact final datum was not present.
|
||||
A child subtree at that key is retained.
|
||||
.It Fn enfilade-get-by-tumbler enfilade tumbler
|
||||
Returns a Scheme list containing the datum at the exact final key, followed by
|
||||
the flattened contents of a child subtree at that key, if present.
|
||||
Missing intermediate children and malformed tumblers raise
|
||||
.Ql TumblerAddressException .
|
||||
A valid final key with nothing stored there returns
|
||||
.Ql () .
|
||||
.It Fn enfilade-get-range enfilade start end
|
||||
Visits numeric keys from
|
||||
.Fa start
|
||||
through
|
||||
.Fa end ,
|
||||
inclusive, and flattens their contents.
|
||||
A negative end means through the end.
|
||||
This is a one-level numeric range, not a dotted tumbler interval.
|
||||
.It Fn tumbler-compare a b
|
||||
Returns a negative integer, zero, or a positive integer according to the
|
||||
whole-tumbler ordering of the two strings.
|
||||
.It Fn tumbler-add position offset
|
||||
Returns the dotted-string result of Xanadu tumbler addition.
|
||||
.It Fn tumbler-subtract a b
|
||||
Returns the generalized non-negative tumbler difference.
|
||||
.El
|
||||
.Sh EXAMPLE
|
||||
.Bd -literal -offset indent
|
||||
(define e (make-enfilade))
|
||||
|
||||
(enfilade-put-data e "1.2.3" 42)
|
||||
(enfilade-put-data e "1.2.4" 43)
|
||||
(enfilade-put-data e "1.3.1" 99)
|
||||
|
||||
(enfilade-get-by-tumbler e "1.2.3")
|
||||
; => (42)
|
||||
|
||||
(enfilade-get-by-tumbler e "1.2")
|
||||
; => (42 43)
|
||||
|
||||
(enfilade-get-range e 1 3)
|
||||
; => (42 43 99)
|
||||
|
||||
(enfilade-remove e "1.2.3")
|
||||
; => #t
|
||||
.Ed
|
||||
.Sh DATUM LIFETIME
|
||||
The C tree does not copy or free Scheme datums.
|
||||
The S7 c-object retains them by marking them during garbage collection.
|
||||
When the c-object is finally freed, the C tree is destroyed.
|
||||
Do not call
|
||||
.Fn destroy_enfilade
|
||||
on the pointer owned by a live S7 c-object.
|
||||
.Sh ERRORS
|
||||
Malformed tumbler strings and missing intermediate children raise
|
||||
.Ql TumblerAddressException .
|
||||
Allocation failures raise
|
||||
.Ql memory-error .
|
||||
Wrong Scheme argument types use S7's normal wrong-type condition.
|
||||
.Sh FILES
|
||||
.Bl -tag -width src/enfilade_s7.c
|
||||
.It Pa src/enfilade_s7.c
|
||||
S7 binding implementation.
|
||||
.It Pa src/logan.c
|
||||
Direct minimal REPL for the module.
|
||||
.It Pa test/enfload.c
|
||||
Non-interactive S7 smoke test.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr tumbler-s7 7 ,
|
||||
.Xr enfilade 3 ,
|
||||
.Xr tumbler 3 ,
|
||||
.Xr ensifer 7
|
||||
.Sh REFERENCES
|
||||
.Lk https://raw.githubusercontent.com/enkiv2/ds-lib/refs/heads/master/Enfilade.py
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
.Dd September 24, 2026
|
||||
.Dt ENFILADE 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm enfilade
|
||||
.Nd small Model-T-style tumbler-addressed enfilade
|
||||
.Sh SYNOPSIS
|
||||
.In enfilade.h
|
||||
.Ft enfilade_t *
|
||||
.Fn create_enfilade void
|
||||
.Ft void
|
||||
.Fn destroy_enfilade "enfilade_t *enf"
|
||||
.Ft enf_result_t
|
||||
.Fn put_enfilade "enfilade_t *enf" "const char *tumbler" "void *datum"
|
||||
.Ft enf_result_t
|
||||
.Fn remove_enfilade "enfilade_t *enf" "const char *tumbler"
|
||||
.Ft void *
|
||||
.Fn getXtumbler_enfilade "enfilade_t *enf" "const char *tumbler"
|
||||
.Ft enf_result_t
|
||||
.Fn getXtumbler_values_enfilade "enfilade_t *enf" "const char *tumbler" "void ***items" "size_t *count"
|
||||
.Ft enf_result_t
|
||||
.Fn getXrange_values_enfilade "enfilade_t *enf" "int64_t start" "int64_t end" "void ***items" "size_t *count"
|
||||
.Ft void
|
||||
.Fn enfilade_visit_data "enfilade_t *enf" "enfilade_datum_visitor visitor" "void *context"
|
||||
.Sh DESCRIPTION
|
||||
An
|
||||
.Nm
|
||||
is a tree whose local keys are unsigned integer tumbler fields.
|
||||
Each node can contain terminal datum values and child enfilades at the same
|
||||
numeric keys.
|
||||
.Pp
|
||||
Putting
|
||||
.Ql 1.2.3
|
||||
creates the path through keys 1, 2, and 3. The datum is stored at the final
|
||||
key.
|
||||
.Pp
|
||||
The interface is intentionally small and follows the behavior of the
|
||||
Model-T-style Python reference implementation used by this project.
|
||||
.Sh CREATION AND LIFETIME
|
||||
.Fn create_enfilade
|
||||
returns an empty enfilade or NULL on allocation failure.
|
||||
.Fn destroy_enfilade
|
||||
recursively frees the enfilade tree.
|
||||
.Pp
|
||||
The enfilade owns its tree nodes but does not own datum pointers supplied by
|
||||
the caller. The caller must keep every datum alive for as long as it may be
|
||||
returned by a lookup and must destroy the enfilade before those datum objects
|
||||
are destroyed.
|
||||
.Sh INSERTION
|
||||
.Fn put_enfilade
|
||||
stores
|
||||
.Fa datum
|
||||
at the exact final field of
|
||||
.Fa tumbler .
|
||||
Missing intermediate child enfilades are created automatically.
|
||||
.Pp
|
||||
A second insertion at the same final key replaces the existing datum.
|
||||
.Pp
|
||||
If a final key already contains a datum and a child subtree, insertion replaces
|
||||
only the datum.
|
||||
.Sh REMOVAL
|
||||
.Fn remove_enfilade
|
||||
removes only the datum stored at the exact final key.
|
||||
.Pp
|
||||
A child subtree at that same key is retained.
|
||||
Empty intermediate child enfilades are pruned after successful removal.
|
||||
The datum pointer itself is never freed.
|
||||
.Pp
|
||||
A missing exact datum returns
|
||||
.Dv ENF_ERR_NOT_FOUND .
|
||||
.Sh EXACT LOOKUP
|
||||
.Fn getXtumbler_enfilade
|
||||
returns only the datum stored at the exact final key, or NULL if none exists or
|
||||
the path is invalid.
|
||||
.Pp
|
||||
For callers that need reference-style flattened lookup,
|
||||
.Fn getXtumbler_values_enfilade
|
||||
walks every component except the last as a child path, then returns a newly
|
||||
allocated array containing the datum at the final key followed by all data in
|
||||
its child subtree.
|
||||
.Pp
|
||||
The caller owns the returned array and releases it with
|
||||
.Xr free 3 .
|
||||
The datum pointers remain owned by the caller.
|
||||
.Sh RANGE
|
||||
.Fn getXrange_values_enfilade
|
||||
walks numeric keys at the current enfilade level from
|
||||
.Fa start
|
||||
through
|
||||
.Fa end ,
|
||||
inclusive, and recursively flattens their contents.
|
||||
A negative
|
||||
.Fa end
|
||||
means there is no upper bound.
|
||||
.Pp
|
||||
This operation deliberately remains the existing
|
||||
.Dq range
|
||||
interface. It is not a parser for dotted tumbler intervals.
|
||||
.Sh TRAVERSAL
|
||||
.Fn enfilade_visit_data
|
||||
visits every stored datum. The visitor callback is not retained after the
|
||||
call.
|
||||
The callback must not be assumed to outlive the traversal call.
|
||||
.Sh EXAMPLE
|
||||
.Bd -literal -offset indent
|
||||
#include "enfilade.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
enfilade_t *e = create_enfilade();
|
||||
int value = 42;
|
||||
void *found;
|
||||
|
||||
if (!e) return 1;
|
||||
if (put_enfilade(e, "1.2.3", &value).code != ENF_OK)
|
||||
return 1;
|
||||
|
||||
found = getXtumbler_enfilade(e, "1.2.3");
|
||||
if (found)
|
||||
printf("%d\\n", *(int *)found);
|
||||
|
||||
destroy_enfilade(e);
|
||||
return 0;
|
||||
}
|
||||
.Ed
|
||||
.Sh ERRORS
|
||||
.Bl -tag -width ENF_ERR_NOT_FOUND
|
||||
.It Dv ENF_OK
|
||||
Success.
|
||||
.It Dv ENF_ERR_NULL
|
||||
A required pointer was null.
|
||||
.It Dv ENF_ERR_ADDR
|
||||
The tumbler text is malformed.
|
||||
.It Dv ENF_ERR_NOT_FOUND
|
||||
A requested datum or intermediate child was absent.
|
||||
.It Dv ENF_ERR_NOMEM
|
||||
An allocation failed.
|
||||
.El
|
||||
.Sh THREADING
|
||||
The enfilade structure does not provide internal locking.
|
||||
External synchronization is required if multiple threads may access the same
|
||||
instance concurrently.
|
||||
.Sh SCOPE
|
||||
This package implements the small tumbler-keyed tree only. It does not
|
||||
implement the broader Green machinery such as WIDs, DISPs, orgls, POOMs,
|
||||
Spanfilades, persistent CRUM storage, rearrangement, or version management.
|
||||
.Sh SEE ALSO
|
||||
.Xr tumbler 3 ,
|
||||
.Xr enfilade-s7 7 ,
|
||||
.Xr ensifer 7
|
||||
.Sh REFERENCES
|
||||
.Lk https://raw.githubusercontent.com/enkiv2/ds-lib/refs/heads/master/Enfilade.py
|
||||
.Pp
|
||||
.Lk https://sentido-labs.com/en/library/201904240732/Xanadu%20Hypertext%20Documents.html
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
.Dd September 24, 2026
|
||||
.Dt ENSIFER 7
|
||||
.Os
|
||||
.ds V 0.1.20
|
||||
.Sh NAME
|
||||
.Nm ensifer
|
||||
.Nd small Model-T-style enfilade for C and S7 Scheme
|
||||
.Sh SYNOPSIS
|
||||
.Bd -literal -offset indent
|
||||
./build/bin/logan
|
||||
|
||||
(define e (make-enfilade))
|
||||
(enfilade-put-data e "1.2.3" 42)
|
||||
(enfilade-get-by-tumbler e "1.2.3")
|
||||
.Ed
|
||||
.Sh DESCRIPTION
|
||||
.Nm ensifer
|
||||
is a small, working enfilade layer intended to be understandable and reusable
|
||||
without requiring the larger Xanadu/Green architecture to be introduced first.
|
||||
.Pp
|
||||
The central idea is simple: a dotted tumbler is a path through an indexed tree.
|
||||
Storing:
|
||||
.Bd -literal -offset indent
|
||||
1.2.3 -> 42
|
||||
.Ed
|
||||
.Pp
|
||||
conceptually produces:
|
||||
.Bd -literal -offset indent
|
||||
root
|
||||
|
|
||||
+-- 1
|
||||
|
|
||||
+-- 2
|
||||
|
|
||||
+-- 3 -> 42
|
||||
.Ed
|
||||
.Pp
|
||||
A node can contain a datum and a child at the same numeric key. Lookup returns
|
||||
the exact-key datum first and then recursively flattens the child.
|
||||
.Sh FIRST USE
|
||||
Configure and build the complete package:
|
||||
.Bd -literal -offset indent
|
||||
./configure
|
||||
make
|
||||
.Ed
|
||||
.Pp
|
||||
Run the REPL, which initializes the enfilade bindings automatically:
|
||||
.Bd -literal -offset indent
|
||||
./build/bin/logan
|
||||
.Ed
|
||||
.Pp
|
||||
Install with
|
||||
.Cm make install .
|
||||
Use
|
||||
.Cm ./configure --prefix=PATH
|
||||
for a non-default prefix, or set
|
||||
.Ev DESTDIR
|
||||
when staging an install.
|
||||
.Sh CORE OPERATIONS
|
||||
The small reference consists of:
|
||||
.Bl -tag -width tumbler-subtract
|
||||
.It Fn make-enfilade
|
||||
Create an empty enfilade.
|
||||
.It Fn enfilade-put-data
|
||||
Insert or replace a datum at a tumbler.
|
||||
.It Fn enfilade-remove
|
||||
Remove the datum at an exact tumbler key.
|
||||
.It Fn enfilade-get-by-tumbler
|
||||
Resolve a dotted tumbler and flatten the matching subtree.
|
||||
.It Fn enfilade-get-range
|
||||
Range over numeric keys at one enfilade level.
|
||||
.It Fn tumbler-compare
|
||||
Compare complete tumbler addresses.
|
||||
.It Fn tumbler-add
|
||||
Perform Xanadu tumbler addition.
|
||||
.It Fn tumbler-subtract
|
||||
Perform the generalized tumbler difference.
|
||||
.El
|
||||
.Sh TUMBLERS
|
||||
Tumblers are unsigned integer fields separated by periods:
|
||||
.Bd -literal -offset indent
|
||||
1
|
||||
1.2
|
||||
1.2.3
|
||||
1.0.7
|
||||
.Ed
|
||||
.Pp
|
||||
The package stores each field as
|
||||
.Vt uint64_t .
|
||||
It does not implement the historical arbitrary-precision Humber encoding.
|
||||
.Pp
|
||||
Whole-tumbler comparison uses the infinitesimal-style order represented by the
|
||||
small Udanax-derived implementations:
|
||||
.Bd -literal -offset indent
|
||||
1 < 1.1 < 1.1.1 < 1.2 < 2
|
||||
.Ed
|
||||
.Pp
|
||||
The C tumbler module also implements Xanadu's non-commutative addition and
|
||||
strong, weak, and generalized subtraction. See
|
||||
.Xr tumbler 3 .
|
||||
.Sh RANGE
|
||||
The name
|
||||
.Dq range
|
||||
is intentionally retained.
|
||||
.Pp
|
||||
.Fn enfilade-get-range
|
||||
operates on the numeric keys of the current enfilade level, with inclusive
|
||||
bounds. A negative upper bound means through the end.
|
||||
.Pp
|
||||
It is not a dotted tumbler interval expression.
|
||||
.Sh REMOVAL
|
||||
.Fn enfilade-remove
|
||||
removes only the datum at the exact final key.
|
||||
If a child subtree exists at that key, it remains.
|
||||
Empty intermediate children are pruned.
|
||||
.Pp
|
||||
In Scheme the operation returns
|
||||
.Ql #t
|
||||
on removal and
|
||||
.Ql #f
|
||||
when there was no datum at the exact key.
|
||||
.Sh DATUM OWNERSHIP
|
||||
C callers own the objects pointed to by datum entries.
|
||||
The enfilade owns only its index nodes and never frees a datum pointer.
|
||||
.Pp
|
||||
In S7, datums are Scheme objects. The c-object mark callback visits every
|
||||
stored datum, making them reachable to the garbage collector while the
|
||||
enfilade remains live.
|
||||
.Sh WHAT THIS IS
|
||||
This is a small tumbler-addressed Model-T-style enfilade.
|
||||
It is intended as a primitive that other structures can use directly.
|
||||
.Pp
|
||||
It is not a complete implementation of Udanax Green.
|
||||
The broader historical system includes specialized enfilades and additional
|
||||
machinery for WIDs, DISPs, rearrangement, versioning, spans, virtual copies,
|
||||
and persistence.
|
||||
.Pp
|
||||
Those concepts are intentionally outside this layer so that the fundamental
|
||||
indexing mechanism remains visible.
|
||||
.Sh REFERENCE IMPLEMENTATION
|
||||
The immediate behavioral reference is the `Enfilade.py` implementation in
|
||||
.Dq ds-lib .
|
||||
.Pp
|
||||
Its `putValue`, `getByTumbler`, `get`, and `getRange` behavior correspond to the
|
||||
operations in this package.
|
||||
.Pp
|
||||
The C `range` implementation deliberately merges a node's value keys and child
|
||||
keys rather than traversing a duplicated combined-key list. Thus a key shared
|
||||
by a datum and child is visited once during a range traversal; the Python
|
||||
reference can visit it twice in that particular case. Exact tumbler lookup
|
||||
retains the reference's datum-then-child result order.
|
||||
.Sh ZIGZAG USE
|
||||
A simple catalogue can use the enfilade without teaching the enfilade anything
|
||||
about cells:
|
||||
.Bd -literal -offset indent
|
||||
cell ID (tumbler)
|
||||
|
|
||||
v
|
||||
enfilade
|
||||
|
|
||||
v
|
||||
cell pointer
|
||||
.Ed
|
||||
.Pp
|
||||
This makes the enfilade a general index beneath a cell store rather than a
|
||||
second representation of the cell itself.
|
||||
.Sh FILES
|
||||
.Bl -tag -width src/enfilade_s7.c
|
||||
.It Pa src/enfilade.c
|
||||
Enfilade tree implementation.
|
||||
.It Pa inc/enfilade.h
|
||||
Public C API.
|
||||
.It Pa src/tumbler.c
|
||||
Tumbler parser, comparison, and arithmetic.
|
||||
.It Pa inc/tumbler.h
|
||||
Public tumbler API.
|
||||
.It Pa src/enfilade_s7.c
|
||||
S7 bindings and c-object type.
|
||||
.It Pa src/logan.c
|
||||
Direct REPL.
|
||||
.It Pa test/enfload.c
|
||||
S7 smoke test.
|
||||
.It Pa scheme/enf.scm
|
||||
Scheme smoke-test procedure.
|
||||
.It Pa test/tumbler-test.c
|
||||
Tumbler regression tests.
|
||||
.It Pa test/enfilade-test.c
|
||||
Enfilade regression tests.
|
||||
.El
|
||||
.Sh VERSION
|
||||
The package version is available as
|
||||
.Dv ENSIFER_VERSION
|
||||
from
|
||||
.Pa inc/enfilade.h
|
||||
and from
|
||||
.Nm logan --version .
|
||||
The current version is 0.1.20.
|
||||
.Sh TRIVIA
|
||||
Ensifer is called after the greatest frost mage on Zuluhed. He would duel glads naked & win.
|
||||
.Sh SEE ALSO
|
||||
.Xr enfilade 3 ,
|
||||
.Xr tumbler 3 ,
|
||||
.Xr enfilade-s7 7 ,
|
||||
.Xr tumbler-s7 7
|
||||
.Sh REFERENCES
|
||||
.Lk https://raw.githubusercontent.com/enkiv2/ds-lib/refs/heads/master/Enfilade.py
|
||||
.Pp
|
||||
.Lk https://sentido-labs.com/en/library/201904240732/Xanadu%20Hypertext%20Documents.html
|
||||
.Pp
|
||||
.Lk https://www.mprove.de/visionreality/media/xuDation.html
|
||||
.Pp
|
||||
.Lk https://pypi.org/project/xanalogica.tumbler/
|
||||
@@ -0,0 +1,55 @@
|
||||
.Dd September 24, 2026
|
||||
.Dt TUMBLER-S7 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm tumbler-s7
|
||||
.Nd S7 Scheme interface to the tumbler primitive
|
||||
.Sh SYNOPSIS
|
||||
Bd -literal -offset indent
|
||||
(load "./build/lib/enfilade.so" (inlet 'init_func 'init_enfilade_s7))
|
||||
|
||||
(tumbler-compare "1" "1.1")
|
||||
(tumbler-add "3" "2.16.3")
|
||||
(tumbler-subtract "1.4.3" "1.1.1")
|
||||
.Ed
|
||||
.Sh DESCRIPTION
|
||||
The module exposes the small C tumbler implementation to S7 without requiring
|
||||
a separate Scheme tumbler object type. Tumblers cross the FFI as dotted
|
||||
unsigned-integer strings.
|
||||
.Sh FUNCTIONS
|
||||
.Bl -tag -width tumbler-subtract
|
||||
.It Fn tumbler-compare a b
|
||||
Returns a negative integer, zero, or a positive integer according to the
|
||||
whole-tumbler ordering of
|
||||
.Fa a
|
||||
and
|
||||
.Fa b .
|
||||
.It Fn tumbler-add position offset
|
||||
Returns a new tumbler string containing Xanadu tumbler addition.
|
||||
.It Fn tumbler-subtract a b
|
||||
Returns the generalized non-negative tumbler difference.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
.Bd -literal
|
||||
(tumbler-compare "1" "1.1")
|
||||
; => -1
|
||||
|
||||
(tumbler-add "3" "2.16.3")
|
||||
; => "5.16.3"
|
||||
|
||||
(tumbler-add "25.6.46.93" "0.0.3.1.21")
|
||||
; => "25.6.49.1.21"
|
||||
|
||||
(tumbler-subtract "1.4.3" "1.1.1")
|
||||
; => "0.3.3"
|
||||
.Ed
|
||||
.Sh ERRORS
|
||||
Malformed tumbler strings raise
|
||||
.Ql TumblerAddressException .
|
||||
Allocation failures raise
|
||||
.Ql memory-error .
|
||||
.Sh SEE ALSO
|
||||
.Xr tumbler 3 ,
|
||||
.Xr enfilade-s7 7
|
||||
.Sh REFERENCES
|
||||
.Lk https://sentido-labs.com/en/library/201904240732/Xanadu%20Hypertext%20Documents.html
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
.Dd September 24, 2026
|
||||
.Dt TUMBLER 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm tumbler
|
||||
.Nd dotted integer coordinates and Xanadu tumbler arithmetic
|
||||
.Sh SYNOPSIS
|
||||
.In tumbler.h
|
||||
.Ft tumbler_result_t
|
||||
.Fn tumbler_parse "const char *text" "tumbler_t *out"
|
||||
.Ft void
|
||||
.Fn tumbler_free "tumbler_t *t"
|
||||
.Ft tumbler_result_t
|
||||
.Fn tumbler_to_string "const tumbler_t *t" "char **out"
|
||||
.Ft int
|
||||
.Fn tumbler_compare "const tumbler_t *a" "const tumbler_t *b"
|
||||
.Ft tumbler_result_t
|
||||
.Fn tumbler_add "const tumbler_t *position" "const tumbler_t *offset" "tumbler_t *out"
|
||||
.Ft tumbler_result_t
|
||||
.Fn tumbler_subtract_strong "const tumbler_t *position" "const tumbler_t *offset" "tumbler_t *out"
|
||||
.Ft tumbler_result_t
|
||||
.Fn tumbler_subtract_weak "const tumbler_t *position" "const tumbler_t *offset" "tumbler_t *out"
|
||||
.Ft tumbler_result_t
|
||||
.Fn tumbler_subtract "const tumbler_t *a" "const tumbler_t *b" "tumbler_t *out"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
module provides the small tumbler primitive used by the enfilade.
|
||||
A tumbler is a finite sequence of unsigned integer fields written in dotted
|
||||
form, such as
|
||||
.Ql 1.2.3 .
|
||||
.Pp
|
||||
The implementation stores each field as a
|
||||
.Vt uint64_t .
|
||||
The historical Xanadu system used arbitrary-precision Humbers; this package
|
||||
intentionally does not implement that representation.
|
||||
.Sh PARSING
|
||||
.Fn tumbler_parse
|
||||
parses one or more unsigned decimal fields separated by periods.
|
||||
On success it allocates the field array in
|
||||
.Vt tumbler_t .
|
||||
The caller must release it with
|
||||
.Fn tumbler_free .
|
||||
.Pp
|
||||
Examples:
|
||||
.Bd -literal -offset indent
|
||||
1
|
||||
1.2
|
||||
1.2.3
|
||||
0.1.0.7
|
||||
.Ed
|
||||
.Pp
|
||||
Malformed input includes an empty string, a leading or trailing period, two
|
||||
periods in succession, non-decimal characters, and negative fields.
|
||||
.Sh COMPARISON
|
||||
.Fn tumbler_compare
|
||||
returns a negative value, zero, or a positive value according as
|
||||
.Fa a
|
||||
is less than, equal to, or greater than
|
||||
.Fa b .
|
||||
.Pp
|
||||
Comparison is performed from the most significant field toward the least
|
||||
significant. A missing field is treated as zero for comparison. Thus:
|
||||
.Bd -literal -offset indent
|
||||
1 < 1.1 < 1.1.1 < 1.2 < 2
|
||||
.Ed
|
||||
.Pp
|
||||
This is the infinitesimal-style tumbler ordering used by the small Udanax-derived
|
||||
tumbler implementations. In this implementation, trailing zero fields do not
|
||||
change comparison order.
|
||||
.Sh ADDITION
|
||||
.Fn tumbler_add
|
||||
implements Xanadu tumbler addition. The operands have different roles:
|
||||
.Fa position
|
||||
is a position and
|
||||
.Fa offset
|
||||
is a distance to move forward.
|
||||
.Pp
|
||||
The procedure is:
|
||||
.Bl -enum
|
||||
.It
|
||||
Copy fields from the position while the corresponding offset fields are zero.
|
||||
.It
|
||||
At the first non-zero offset field, add it to the corresponding position field.
|
||||
.It
|
||||
Copy the remaining offset fields into the result.
|
||||
.El
|
||||
.Pp
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
3 + 2.16.3
|
||||
= 5.16.3
|
||||
|
||||
25.6.46.93 + 0.0.3.1.21
|
||||
= 25.6.49.1.21
|
||||
.Ed
|
||||
.Pp
|
||||
Addition is non-commutative.
|
||||
.Sh SUBTRACTION
|
||||
The historical system distinguishes strong and weak subtraction.
|
||||
.Fn tumbler_subtract_strong
|
||||
implements strong subtraction and
|
||||
.Fn tumbler_subtract_weak
|
||||
implements weak subtraction.
|
||||
.Pp
|
||||
Strong subtraction preserves matching leading fields as zero until the first
|
||||
difference, subtracts there, then copies the remaining fields from the
|
||||
position. It requires
|
||||
.Fa position
|
||||
not be less than
|
||||
.Fa offset .
|
||||
.Pp
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
1.4.3 s- 1.1.1
|
||||
= 0.3.3
|
||||
|
||||
0.1.2.3.4.5.6 s- 0.1.2.3.3.3.3
|
||||
= 0.0.0.0.1.5.6
|
||||
.Ed
|
||||
.Pp
|
||||
Weak subtraction preserves the position through leading zero offset fields,
|
||||
then subtracts the first non-zero offset field and stops.
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
1.4.3 w- 1.1.1
|
||||
= 0
|
||||
|
||||
0.3.3.3.4.5.6 w- 0.1.1.3.3.3.3
|
||||
= 0.2
|
||||
.Ed
|
||||
.Pp
|
||||
.Fn tumbler_subtract
|
||||
is the generalized difference operation used by the Xanadu system. If
|
||||
.Fa a
|
||||
is greater than
|
||||
.Fa b ,
|
||||
strong subtraction computes
|
||||
.Ql a - b .
|
||||
Otherwise weak subtraction computes
|
||||
.Ql b - a .
|
||||
The result is therefore never negative.
|
||||
.Sh MEMORY
|
||||
All parsed and arithmetic result tumblers own heap storage for their digit
|
||||
arrays. The caller is responsible for calling
|
||||
.Fn tumbler_free .
|
||||
.Pp
|
||||
Strings returned by
|
||||
.Fn tumbler_to_string
|
||||
are allocated with
|
||||
.Xr malloc 3
|
||||
and are released with
|
||||
.Xr free 3 .
|
||||
.Sh ERRORS
|
||||
The result structure uses these codes:
|
||||
.Bl -tag -width TUMBLER_ERR_UNDERFLOW
|
||||
.It Dv TUMBLER_OK
|
||||
Success.
|
||||
.It Dv TUMBLER_ERR_NULL
|
||||
A required pointer was null.
|
||||
.It Dv TUMBLER_ERR_INVALID
|
||||
The tumbler representation is malformed.
|
||||
.It Dv TUMBLER_ERR_NOMEM
|
||||
Memory allocation failed.
|
||||
.It Dv TUMBLER_ERR_OVERFLOW
|
||||
An integer field or arithmetic result exceeded
|
||||
.Vt uint64_t .
|
||||
.It Dv TUMBLER_ERR_UNDERFLOW
|
||||
A subtraction would require a negative field.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr enfilade 3 ,
|
||||
.Xr tumbler-s7 7 ,
|
||||
.Xr enfilade-s7 7
|
||||
.Sh REFERENCES
|
||||
.Pp
|
||||
.Lk https://sentido-labs.com/en/library/201904240732/Xanadu%20Hypertext%20Documents.html
|
||||
.Pp
|
||||
.Lk https://pypi.org/project/xanalogica.tumbler/
|
||||
Reference in new issue
Block a user