b034f5705a14 — Leonard Ritter tip 22 days ago
* UVM: renamed table to cell, string to text, added blob type
4 files changed, 510 insertions(+), 114 deletions(-)

A => lib/tukan/module.sc
M lib/tukan/pickle.sc
M lib/tukan/uvm.sc
M testing/test_uvm.sc
A => lib/tukan/module.sc +384 -0
@@ 0,0 1,384 @@ 
+using import struct
+using import enum
+using import Set
+using import String
+
+using import .uvm
+
+# module serialization
+###############################################################################
+
+let
+    DB_ALIGNMENT = 8:u32
+
+inline align-size (offset align)
+    (offset + align - 1) & (~ (align - 1))
+
+let db = (import tukan.db)
+
+enum ModuleError
+    error : Error
+    database : db.Error
+
+    #inline handle (self)
+        fprintf (stderr) "DB error: %s\n" (mdb_strerror (storagecast self))
+        abort;
+
+    @@ memo
+    inline __ras (T cls)
+        static-if (T == db.Error)
+            fn (self)
+                this-type.database self
+        elseif (T == Error)
+            fn (self)
+                this-type.error self
+
+    @@ memo
+    inline __as (cls T)
+        static-if (T == Error)
+            fn (self)
+                dispatch self
+                case error (err)
+                    copy err
+                case database (err)
+                    err as Error
+                default
+                    sc_error_new (repr self)
+
+let
+    DBI_BLOB = "blob"
+    #DBI_HASH = "hash"
+
+    MAX_DBS = 4
+
+fn... format-hex-memory
+case (inbytes : rawstring, sz : usize)
+    let sz2 = (sz * 2)
+    let str = (alloca-array i8 sz2)
+    inline conv (x)
+        (+ x (? (x < 10:u8) 48:u8 87:u8)) as i8
+    for i in (range sz)
+        c := ((deref (inbytes @ i)) as u8)
+        i := i << 1
+        str @ i = (conv ((c >> 4:u8) & 0xf:u8))
+        str @ (i + 1) = (conv (c & 0xf:u8))
+    string str sz2
+case (value : db.Value)
+    let sz ptr = (unpack value)
+    this-function (ptr as rawstring) sz
+
+struct Databases plain
+    blob : db.Index
+    #hash : db.Index
+
+type Act < Struct
+
+struct ViewAct < Act
+    _txn : db.Transaction
+    _db : Databases
+
+    inline __typecall (cls txn db)
+        super-type.__typecall cls
+            _txn = txn
+            _db = db
+
+    inline __drop (self)
+        'abort self._txn
+
+    inline abort (self)
+        'abort self._txn
+        lose self
+
+    inline commit (self)
+        'abort self._txn
+        lose self
+
+struct EditAct < Act
+    _txn : db.Transaction
+    _db : Databases
+
+    inline __typecall (cls txn db)
+        super-type.__typecall cls
+            _txn = txn
+            _db = db
+
+    inline __drop (self)
+        'abort self._txn
+
+    inline abort (self)
+        'abort self._txn
+        lose self
+
+    inline commit (self)
+        try
+            'commit self._txn
+        except (err)
+            raise (err as ModuleError)
+        lose self
+
+struct Module
+    _env : db.Environment
+    _db : Databases
+
+    fn from-path (path)
+        let env =
+            try (db.Environment)
+            except (err)
+                raise (err as ModuleError)
+        try
+            'set-maxdbs env MAX_DBS
+            'open env path db.NoSubDir
+            let txn = ('begin env)
+            try
+                let dbi_blob =
+                    'open txn DBI_BLOB db.Create
+                #let dbi_hash =
+                    'open txn DBI_HASH db.Create
+                'commit txn
+                return
+                    super-type.__typecall this-type
+                        _env = env
+                        _db =
+                            Databases
+                                blob = dbi_blob
+                                #hash = dbi_hash
+                                #edge = dbi_edge
+            except (err)
+                'abort txn
+                raise err
+        except (err)
+            'close env
+            raise (err as ModuleError)
+
+    inline __typecall (cls path)
+        from-path path
+
+    inline view (self)
+        let txn =
+            try ('begin self._env db.ReadOnly)
+            except (err) (raise (err as ModuleError))
+        ViewAct txn self._db
+
+    inline edit (self)
+        let txn =
+            try ('begin self._env)
+            except (err) (raise (err as ModuleError))
+        EditAct txn self._db
+
+    inline __drop (self)
+        'close self._env
+
+# declare void @llvm.memcpy.p0i8.p0i8.i64(i8* <dest>, i8* <src>,
+                                        i64 <len>, i1 <isvolatile>)
+let llvm.memcpy.p0i8.p0i8.i64 =
+    extern 'llvm.memcpy.p0i8.p0i8.i64
+        function void (mutable rawstring) rawstring usize bool
+
+let stringreader =
+    inline (data size source offset contentsize act cache)
+        assert ((offset + size) <= contentsize) "buffer underflow"
+        llvm.memcpy.p0i8.p0i8.i64 (data as (mutable rawstring))
+            & (source @ offset)
+            size
+            false
+        offset += size
+
+let hashstringreader =
+    inline (data size source offset contentsize act cache)
+        raising (uniqueof ModuleError -1)
+        assert ((offset + size) <= contentsize) "buffer underflow"
+        llvm.memcpy.p0i8.p0i8.i64 (data as (mutable rawstring))
+            & (source @ offset)
+            size
+            false
+        offset += size
+        let digest = (@ (data as @Atom.DigestType))
+        if ('in? cache digest)
+            return;
+        # load recursively
+        let key = (db.Value size data)
+        'insert cache ('load1 act key cache)
+        ;
+
+let stringwriter =
+    inline (data size dest)
+        'append dest (String (data as rawstring) size)
+
+inline dbkey (value)
+    local digest = ('uhash value)
+    let digestsize = (sizeof digest)
+    static-assert ((digestsize % DB_ALIGNMENT) == 0)
+    db.Value digestsize &digest
+
+type+ Act
+    fn... load1 (self, key : db.Value, cache)
+        returning (uniqueof Atom -1)
+        raising (uniqueof ModuleError -1)
+        let db_blob = self._db.blob
+
+        let content =
+            try ('get self._txn self._db.blob key)
+            except (err) (raise (err as ModuleError))
+
+        let contentsize source = (unpack content)
+        let source = (source as rawstring)
+        local offset : usize = 0
+
+        let ctx... = source offset contentsize self cache
+
+        #local digest : SHA256.DigestType
+        #hashfilereader (&digest as rawstring) (sizeof digest) file lut lutsize true
+        local kind : u8
+        stringreader &kind (sizeof kind) ctx...
+
+        inline unpickle-str ()
+            local size : u64
+            stringreader (&size as rawstring) (sizeof size) ctx...
+            let sz = (deref size)
+            local str = (String sz)
+            'resize str sz
+            stringreader (str as rawstring) sz ctx...
+            str
+
+        #let kind = (Atom.kind-from-digest digest)
+        let kind = (kind as i32 as Atom.Kind)
+        let atom =
+            switch kind
+            case Atom.Kind.None (Atom)
+            case Atom.Kind.False (Atom false)
+            case Atom.Kind.True (Atom true)
+            case Atom.Kind.Number
+                local num = ((Number.reader stringreader) ctx...)
+                Atom num
+            pass Atom.Kind.Blob
+            pass Atom.Kind.Text
+            pass Atom.Kind.Symbol
+            do
+                Atom.wrap (UString (unpickle-str)) kind
+            case Atom.Kind.CellLimb
+                Atom ((CellLimb.reader stringreader hashstringreader) cache ctx...)
+            case Atom.Kind.Cell
+                Atom ((Cell.reader stringreader hashstringreader) cache ctx...)
+            default
+                assert false "unhandled atom kind"
+                unreachable;
+        #assert (atom == digest)
+        atom
+
+    fn load (self)
+        let key = (dbkey (Atom))
+        let content =
+            try ('get self._txn self._db.blob key)
+            except (err) (raise (err as ModuleError))
+        let digestsize digest = (unpack content)
+        assert (digestsize == (sizeof Atom.DigestType))
+
+        local cache :
+            Set Atom
+                inline (value)
+                    static-if ((typeof value) == Atom.DigestType)
+                        Atom.hash-from-digest value
+                    else
+                        hash value
+        va-map
+            inline (value)
+                'insert cache value
+            Atom;
+            Atom false
+            Atom true
+
+        load1 self content cache
+
+type+ EditAct
+    fn... store1 (self, key : db.Value, value : Atom)
+        let db_blob = self._db.blob
+
+        local blob : String
+
+        #local digest : SHA256.DigestType = ('uhash value)
+        #hashfilewriter (&digest as rawstring) (sizeof digest) file lut lutsize true
+        local kind : u8 = (('kind value) as integer as u8)
+        stringwriter &kind (sizeof kind) blob
+
+        inline pickle-str (str)
+            let sz = (countof str)
+            local size : u64 = sz
+            stringwriter (&size as rawstring) (sizeof size) blob
+            stringwriter (str as rawstring) sz blob
+
+        dispatch value
+        case Number (num)
+            (Number.writer stringwriter) num blob
+        case Blob (str)
+            pickle-str str
+        case Text (str)
+            pickle-str str
+        case Symbol (str)
+            pickle-str str
+        case CellLimb (limb)
+            (CellLimb.writer stringwriter stringwriter) limb blob
+        case Cell (table)
+            (Cell.writer stringwriter stringwriter) table blob
+        default;
+
+        let PADSTR = "\0\0\0\0\0\0\0\0"
+        #static-assert ((countof PADSTR) == DB_ALIGNMENT)
+        'append blob PADSTR
+        let value = (db.Value (align-size (countof blob) DB_ALIGNMENT) (& (blob @ 0)))
+        try ('put self._txn db_blob key value)
+        except (err) (raise (err as ModuleError))
+        ;
+
+    fn... store (self, root : Atom, root? : bool = true)
+        local done : (Set Atom)
+        va-map
+            inline (value)
+                local key = ('hashbits value)
+                'insert done value
+            Atom;
+            Atom false
+            Atom true
+
+        fn recur (value ...)
+            let self done = ...
+            let recur = this-function
+            if ('in? done value)
+                return;
+            'insert done (copy value)
+
+            let key = (dbkey value)
+            let db_blob = self._db.blob
+            try
+                # if this passes, the key already exists
+                'get self._txn db_blob key
+                return;
+            except (err)
+                if (err != db.NotFound)
+                    raise (err as ModuleError)
+
+            assert ('in? done value)
+            dispatch value
+            case CellLimb (limb)
+                for value in limb.cells
+                    recur value ...
+            case Cell (table)
+                recur table.meta ...
+                recur table.ivalues ...
+                recur table.keys ...
+                recur table.values ...
+            default;
+            'store1 self key value
+            ;
+        recur root self done
+        if root?
+            let key = (dbkey (Atom))
+            let value = (dbkey root)
+            try ('put self._txn self._db.blob key value)
+            except (err) (raise (err as ModuleError))
+
+###############################################################################
+
+do
+    let Module
+
+    locals;

          
M lib/tukan/pickle.sc +17 -14
@@ 64,14 64,16 @@ fn... pickle1 (file, lut, lutsize, value
     dispatch value
     case Number (num)
         (Number.writer filewriter) num file lut lutsize
-    case String (str)
+    case Blob (str)
+        pickle-str str
+    case Text (str)
         pickle-str str
     case Symbol (str)
         pickle-str str
-    case TableLimb (limb)
-        (TableLimb.writer filewriter hashfilewriter) limb file lut lutsize
-    case Table (table)
-        (Table.writer filewriter hashfilewriter) table file lut lutsize
+    case CellLimb (limb)
+        (CellLimb.writer filewriter hashfilewriter) limb file lut lutsize
+    case Cell (table)
+        (Cell.writer filewriter hashfilewriter) table file lut lutsize
     default;
 
 fn unpickle1 (file lut lutsize cache)

          
@@ 99,14 101,15 @@ fn unpickle1 (file lut lutsize cache)
         case Atom.Kind.Number
             local num = ((Number.reader filereader) file lut lutsize)
             Atom num
-        case Atom.Kind.String
-            Atom (unpickle-str)
-        case Atom.Kind.Symbol
+        pass Atom.Kind.Text
+        pass Atom.Kind.Blob
+        pass Atom.Kind.Symbol
+        do
             Atom.wrap (UString (unpickle-str)) kind
-        case Atom.Kind.TableLimb
-            Atom ((TableLimb.reader filereader hashfilereader) cache file lut lutsize)
-        case Atom.Kind.Table
-            Atom ((Table.reader filereader hashfilereader) cache file lut lutsize)
+        case Atom.Kind.CellLimb
+            Atom ((CellLimb.reader filereader hashfilereader) cache file lut lutsize)
+        case Atom.Kind.Cell
+            Atom ((Cell.reader filereader hashfilereader) cache file lut lutsize)
         default
             assert false "unhandled atom kind"
             unreachable;

          
@@ 135,10 138,10 @@ fn... pickle (file, root : Atom)
         'insert done (copy value)
         assert ('in? done value)
         dispatch value
-        case TableLimb (limb)
+        case CellLimb (limb)
             for value in limb.cells
                 recur value ...
-        case Table (table)
+        case Cell (table)
             recur table.meta ...
             recur table.ivalues ...
             recur table.keys ...

          
M lib/tukan/uvm.sc +103 -94
@@ 11,7 11,7 @@ using import .thread
 using import .SHA256
 
 #   features:
-    * six value types: none, bool, number, string (text?), symbol, cell
+    * seven value types: none, bool, number, text, blob, symbol, cell
     * a pure functional evaluator without mutation, recursion or other side effects
     * GC free, refcount based memory management
     * any value can be used as a key in a map

          
@@ 297,7 297,7 @@ type Atom :: voidstar
     let DigestType = Hasher.DigestType
     let IntDigestType = u256
 
-# Table
+# Cell
 ################################################################################
 
 let IndexBits = 4

          
@@ 309,7 309,7 @@ fn depth-maxindex (depth)
     ((ArrayCellCount as u64) << (depth * IndexBits)) - 1
 
 @@ verify-sizeof 136
-struct TableLimb
+struct CellLimb
     cells : (array Atom ArrayCellCount)
     mask : u64 = 0 # slots used
 

          
@@ 346,7 346,7 @@ struct TableLimb
                                 copy
                                     'get cache celldigest
                             else
-                                report "TableLimb cell missing:"
+                                report "CellLimb cell missing:"
                                     sha256-digest-string celldigest
                                     \ "(" (Atom.kind-from-digest celldigest) ")"
                                 Atom;

          
@@ 373,10 373,10 @@ struct TableLimb
             cells = (typeinit (va-map copy (unpack self.cells)))
             mask = self.mask
 
-    fn __repr (self) "<TableLimb>"
+    fn __repr (self) "<CellLimb>"
 
 @@ verify-sizeof 40
-struct Table
+struct Cell
     meta : Atom
     keys : Atom
     values : Atom

          
@@ 422,7 422,7 @@ struct Table
                                 copy
                                     'get cache memberdigest
                             else
-                                report "Table member missing"
+                                report "Cell member missing"
                                     sha256-digest-string memberdigest
                                     \ "(" (Atom.kind-from-digest memberdigest) ")"
                                 Atom;

          
@@ 451,7 451,7 @@ struct Table
             fn recur (node depth index ...)
                 returning void
                 dispatch node
-                case TableLimb (limb)
+                case CellLimb (limb)
                     let maxindex = (depth-maxindex depth)
                     let slot-capacity = ((maxindex >> IndexBits) + 1)
                     for i in (range ArrayCellCount)

          
@@ 472,9 472,9 @@ struct Table
             fn recur (key value ...)
                 returning void
                 dispatch key
-                case TableLimb (klimb)
+                case CellLimb (klimb)
                     dispatch value
-                    case TableLimb (vlimb)
+                    case CellLimb (vlimb)
                         let kl = klimb.cells
                         let vl = vlimb.cells
                         for i in (range ArrayCellCount)

          
@@ 490,7 490,7 @@ struct Table
                     f key value ...
             recur table.keys table.values ...
 
-    fn __repr (self) "<Table>"
+    fn __repr (self) "<Cell>"
 
     fn capacity (self)
         depth-maxindex (copy self.depth)

          
@@ 529,7 529,7 @@ struct Table
                             let newdepth = (depth + 1)
                             maxindex := (depth-maxindex newdepth)
                             # split
-                            local newlimb = (TableLimb)
+                            local newlimb = (CellLimb)
                             newlimb.mask = 1
                             newlimb.cells @ 0 = node
                             for i in (range 1 ArrayCellCount)

          
@@ 544,11 544,11 @@ struct Table
 
             let newlimb =
                 dispatch node
-                case TableLimb (limb)
+                case CellLimb (limb)
                     local newlimb = (copy limb)
                 default
                     # split
-                    local newlimb : TableLimb
+                    local newlimb : CellLimb
                     if (not ('none? node))
                         newlimb.mask = 1
                         newlimb.cells @ 0 = node

          
@@ 586,7 586,7 @@ struct Table
         let node depth =
             loop (node depth = (recur self.ivalues (copy self.depth) index value))
                 dispatch node
-                case TableLimb (limb)
+                case CellLimb (limb)
                     if (limb.mask == 1)
                         repeat
                             copy (limb.cells @ 0)

          
@@ 613,7 613,7 @@ struct Table
                 return (Atom)
 
             dispatch node
-            case TableLimb (limb)
+            case CellLimb (limb)
                 let slot-capacity = ((maxindex >> IndexBits) + 1)
                 let slot-index = (index // slot-capacity)
                 assert (slot-index < ArrayCellCount)

          
@@ 637,7 637,7 @@ struct Table
             return 0:u64
         loop (node depth index = (deref self.ivalues) (copy self.depth) 0:u64)
             dispatch node
-            case TableLimb (limb)
+            case CellLimb (limb)
                 repeat
                     label found
                         for i in (rrange ArrayCellCount)

          
@@ 661,7 661,7 @@ struct Table
     fn get-meta (self) self.meta
 
     fn... set-meta (self, metatable : Atom)
-        assert (('kind metatable) == Atom.Kind.Table)
+        assert (('kind metatable) == Atom.Kind.Cell)
         local self = (copy self)
         self.meta = metatable
         self

          
@@ 671,10 671,10 @@ struct Table
             returning (uniqueof Atom -1) (uniqueof Atom -2)
             assert (depth <= 56)
             let mask = (((('hashbits key) >> (depth * IndexBits)) as u32) & IndexMask)
-            if (('kind keylimb) == Atom.Kind.TableLimb) # branch
-                assert (('kind valuelimb) == Atom.Kind.TableLimb)
-                local newkl = (copy (keylimb as TableLimb))
-                local newvl = (copy (valuelimb as TableLimb))
+            if (('kind keylimb) == Atom.Kind.CellLimb) # branch
+                assert (('kind valuelimb) == Atom.Kind.CellLimb)
+                local newkl = (copy (keylimb as CellLimb))
+                local newvl = (copy (valuelimb as CellLimb))
                 let currentkey = (copy (newkl.cells @ mask))
                 let subkeylimb subvaluelimb =
                     this-function currentkey (newvl.cells @ mask) key value (depth + 1)

          
@@ 707,7 707,7 @@ struct Table
                     # we're removing this value anyway
                     return (copy keylimb) (copy valuelimb)
                 let oldmask = (((('hashbits keylimb) >> (depth * IndexBits)) as u32) & IndexMask)
-                local limb : TableLimb
+                local limb : CellLimb
                 for i in (range ArrayCellCount)
                     limb.cells @ i = (Atom)
                 limb.mask = 1:u64 << oldmask

          
@@ 758,9 758,9 @@ struct Table
         fn recur (keylimb valuelimb key depth)
             returning (uniqueof Atom -1)
             let mask = (((('hashbits key) >> (depth * IndexBits)) as u32) & IndexMask)
-            if (('kind keylimb) == Atom.Kind.TableLimb) # branch
-                local newkl = (copy (keylimb as TableLimb))
-                local newvl = (copy (valuelimb as TableLimb))
+            if (('kind keylimb) == Atom.Kind.CellLimb) # branch
+                local newkl = (copy (keylimb as CellLimb))
+                local newvl = (copy (valuelimb as CellLimb))
                 return
                     this-function (newkl.cells @ mask) (newvl.cells @ mask) key (depth + 1)
             elseif (keylimb == key) # found key

          
@@ 825,8 825,8 @@ let
                 'hash sha (self as rawstring) (countof self)
                 'digest sha digest
                 digest
-    UTableLimb = (UType TableLimb)
-    UTable = (UType Table)
+    UCellLimb = (UType CellLimb)
+    UCell = (UType Cell)
 
 type+ Atom
     enum Kind plain

          
@@ 834,10 834,11 @@ type+ Atom
         False
         True
         Number
-        String
+        Blob
+        Text
         Symbol
-        TableLimb
-        Table
+        CellLimb
+        Cell
 
     inline __typecall (cls value)
         imply value cls

          
@@ 868,15 869,15 @@ type+ Atom
         elseif (T == Symbol)
             inline (self)
                 wrap (UString (String (self as string))) Kind.Symbol
-        elseif (T == TableLimb)
+        elseif (T == CellLimb)
             inline (self)
-                wrap (UTableLimb self) Kind.TableLimb
-        elseif (T == Table)
+                wrap (UCellLimb self) Kind.CellLimb
+        elseif (T == Cell)
             inline (self)
-                wrap (UTable self) Kind.Table
+                wrap (UCell self) Kind.Cell
         elseif ((imply? T String) or (T == string))
             inline (self)
-                wrap (UString (self as String)) Kind.String
+                wrap (UString (self as String)) Kind.Text
         elseif (T == Nothing)
             inline (self)
                 wrap (null as voidstar) Kind.None

          
@@ 903,14 904,14 @@ type+ Atom
             inline (self)
                 assert ((kind self) == Kind.Number)
                 (bitcast (topointer self) UNumber) . data
-        elseif (T == TableLimb)
+        elseif (T == CellLimb)
             inline (self)
-                assert ((kind self) == Kind.TableLimb)
-                (bitcast (topointer self) UTableLimb) . data
-        elseif (T == Table)
+                assert ((kind self) == Kind.CellLimb)
+                (bitcast (topointer self) UCellLimb) . data
+        elseif (T == Cell)
             inline (self)
-                assert ((kind self) == Kind.Table)
-                (bitcast (topointer self) UTable) . data
+                assert ((kind self) == Kind.Cell)
+                (bitcast (topointer self) UCell) . data
         elseif (T == String)
             inline (self)
                 switch (kind self)

          
@@ 934,7 935,7 @@ type+ Atom
         case list
             let l = (value as list)
             let t =
-                fold (t = (Table)) for i elem in (enumerate l)
+                fold (t = (Cell)) for i elem in (enumerate l)
                     label done
                         if (('typeof elem) == list)
                             elem as:= list

          
@@ 1011,13 1012,14 @@ type+ Atom
             case Kind.Number
                 copy ((bitcast ptr UNumber) . id)
             pass Kind.Symbol
-            pass Kind.String
+            pass Kind.Blob
+            pass Kind.Text
             do
                 copy ((bitcast ptr UString) . id)
-            case Kind.TableLimb
-                copy ((bitcast ptr UTableLimb) . id)
-            case Kind.Table
-                copy ((bitcast ptr UTable) . id)
+            case Kind.CellLimb
+                copy ((bitcast ptr UCellLimb) . id)
+            case Kind.Cell
+                copy ((bitcast ptr UCell) . id)
             default
                 nullof SHA256.DigestType
         # embed kind bits into most significant bits of digest

          
@@ 1064,14 1066,15 @@ type+ Atom
                 switch val
                 case Kind.Number
                     append-case `((bitcast ptr UNumber) . data)
-                pass Kind.String
+                pass Kind.Blob
+                pass Kind.Text
                 pass Kind.Symbol
                 do
                     append-case `((bitcast ptr UString) . data)
-                case Kind.TableLimb
-                    append-case `((bitcast ptr UTableLimb) . data)
-                case Kind.Table
-                    append-case `((bitcast ptr UTable) . data)
+                case Kind.CellLimb
+                    append-case `((bitcast ptr UCellLimb) . data)
+                case Kind.Cell
+                    append-case `((bitcast ptr UCell) . data)
                 default
                     append-case;
         spice-quote

          
@@ 1086,15 1089,16 @@ type+ Atom
             case False () "false"
             case True () "true"
             case Number (n) (repr n)
-            case String (n) (repr (n as string))
+            case Text (n) (repr (n as string))
+            case Blob (n) (repr (n as string))
             case Symbol (n) (n as string)
-            case TableLimb (n)
+            case CellLimb (n)
                 local digest = ('uhash self)
                 ..
                     tostring ('kind self)
                     " "
                     (sha256-digest-string digest) as string
-            case Table (n)
+            case Cell (n)
                 local digest = ('uhash self)
                 ..
                     tostring ('kind self)

          
@@ 1119,7 1123,11 @@ type+ Atom
             let str = (('tostring num) as string)
             return
                 String (default-styler style-number str)
-        case String (str)
+        case Text (str)
+            let str = (str as string)
+            String
+                repr str
+        case Blob (str)
             let str = (str as string)
             String
                 repr str

          
@@ 1127,11 1135,11 @@ type+ Atom
             let str = (Symbol (str as string))
             String
                 repr str
-        case Table (table)
+        case Cell (table)
             local str : String
             'append str "("
             let f =
-                Table.gen-each-index
+                Cell.gen-each-index
                     inline (index node str count)
                         if (count > 0)
                             'append str " "

          
@@ 1149,7 1157,7 @@ type+ Atom
             local count = 0
             f table str count
             let f =
-                Table.gen-each-pair
+                Cell.gen-each-pair
                     inline (key value str count)
                         if (count > 0)
                             'append str " "

          
@@ 1182,13 1190,14 @@ type+ Atom
         case Kind.Number
             __drop (bitcast ptr UNumber)
         pass Kind.Symbol
-        pass Kind.String
+        pass Kind.Blob
+        pass Kind.Text
         do
             __drop (bitcast ptr UString)
-        case Kind.TableLimb
-            __drop (bitcast ptr UTableLimb)
-        case Kind.Table
-            __drop (bitcast ptr UTable)
+        case Kind.CellLimb
+            __drop (bitcast ptr UCellLimb)
+        case Kind.Cell
+            __drop (bitcast ptr UCell)
         default;
 
     fn none? (self)

          
@@ 1203,7 1212,7 @@ sugar uquote (expr...)
 ################################################################################
 
 let builtins global-env =
-    fold (scope env = (Scope) (Table)) for name in
+    fold (scope env = (Scope) (Cell)) for name in
         sugar-quote + - * / let fn quote set get nextindex _
         sym := (Atom (name as Symbol))
         code := ('hashbits sym)

          
@@ 1216,7 1225,7 @@ global global-env : Atom = global-env
 run-stage;
 
 global mt_closure : Atom =
-    Table.new
+    Cell.new
         type = "closure"
 
 fn global-environment ()

          
@@ 1225,29 1234,29 @@ fn global-environment ()
 fn... ueval (env : Atom, expr : Atom)
     let ueval = this-function
 
-    assert (('kind env) == Atom.Kind.Table)
-    let envtable = (env as Table)
+    assert (('kind env) == Atom.Kind.Cell)
+    let envtable = (env as Cell)
     switch ('kind expr)
     case Atom.Kind.Symbol
         return ('get envtable expr)
-    case Atom.Kind.Table
+    case Atom.Kind.Cell
         let head =
-            ueval env ('get-index (expr as Table) 0)
+            ueval env ('get-index (expr as Cell) 0)
         switch ('kind head)
         case Atom.Kind.Symbol
-            let args = (expr as Table)
+            let args = (expr as Cell)
 
             switch ('hashbits head)
             # (let (: k v) ... expr)
             case builtins.let
                 let f =
-                    Table.gen-each-pair
+                    Cell.gen-each-pair
                         inline (k v origenv env)
                             env =
                                 'set env (copy k)
                                     ueval origenv (copy v)
                             ;
-                local newenv = (copy (env as Table))
+                local newenv = (copy (env as Cell))
                 f args env newenv
                 return (ueval newenv ('get args 1))
             # (fn (param ...) expr)

          
@@ 1255,7 1264,7 @@ fn... ueval (env : Atom, expr : Atom)
                 return
                     Atom
                         'set-meta
-                            Table.new (copy env) (copy expr)
+                            Cell.new (copy env) (copy expr)
                             copy mt_closure
             # (quote value)
             case builtins.quote

          
@@ 1266,17 1275,17 @@ fn... ueval (env : Atom, expr : Atom)
         # evaluate all table elements
         let args =
             do
-                local args = (Table)
-                let table = (expr as Table)
+                local args = (Cell)
+                let table = (expr as Cell)
                 call
-                    Table.gen-each-index
+                    Cell.gen-each-index
                         inline (index node result env)
                             if (index > 0)
                                 result =
                                     'set-index result (index - 1) (ueval env node)
                     \ table args env
                 call
-                    Table.gen-each-pair
+                    Cell.gen-each-pair
                         inline (key value result env)
                             result =
                                 'set result

          
@@ 1287,17 1296,17 @@ fn... ueval (env : Atom, expr : Atom)
                 deref args
 
         switch ('kind head)
-        case Atom.Kind.Table
-            let headtable = (head as Table)
+        case Atom.Kind.Cell
+            let headtable = (head as Cell)
             if (('get-meta headtable) == mt_closure)
                 let origenv = env
-                local env = (copy (('get-index headtable 0) as Table))
+                local env = (copy (('get-index headtable 0) as Cell))
                 let f = ('get-index headtable 1)
-                let ftable = (f as Table)
+                let ftable = (f as Cell)
                 let params = ('get-index ftable 1)
-                let tparams = (params as Table)
+                let tparams = (params as Cell)
                 let eachf =
-                    Table.gen-each-index
+                    Cell.gen-each-index
                         inline (i name origenv env args)
                             let value = ('get-index args i)
                             env =

          
@@ 1306,14 1315,14 @@ fn... ueval (env : Atom, expr : Atom)
                                         'get args name
                                     else value
                             ;
-                eachf tparams origenv env (args as Table)
+                eachf tparams origenv env (args as Cell)
                 let expr = ('get-index ftable 2)
                 return (ueval env expr)
             else
                 print "cannot apply table:" ('tostring expr)
                 return (Atom)
         case Atom.Kind.Symbol
-            let table = (expr as Table)
+            let table = (expr as Cell)
 
             fn verify (val K)
                 if (('kind val) != K)

          
@@ 1335,16 1344,16 @@ fn... ueval (env : Atom, expr : Atom)
 
             inline eval-set ()
                 let source = ('get-index args 0)
-                verify source Atom.Kind.Table
-                source as:= Table
+                verify source Atom.Kind.Cell
+                source as:= Cell
                 let key = ('get-index args 1)
                 let value = ('get-index args 2)
                 Atom ('set source key value)
 
             inline eval-get ()
                 let source = ('get-index args 0)
-                verify source Atom.Kind.Table
-                source as:= Table
+                verify source Atom.Kind.Cell
+                source as:= Cell
                 let key = ('get-index args 1)
                 let value = ('get-index args 2)
                 let result = ('get source key)

          
@@ 1355,8 1364,8 @@ fn... ueval (env : Atom, expr : Atom)
 
             inline eval-countof ()
                 let source = ('get-index args 0)
-                verify source Atom.Kind.Table
-                source as:= Table
+                verify source Atom.Kind.Cell
+                source as:= Cell
                 Atom ('next-index source)
 
             switch ('hashbits head)

          
@@ 1384,7 1393,7 @@ fn... ueval (env : Atom, expr : Atom)
 ###############################################################################
 
 do
-    let Number Table Atom UString TableLimb
+    let Number Cell Atom UString CellLimb
     let uquote
     let global-environment ueval
 

          
M testing/test_uvm.sc +6 -6
@@ 21,9 21,9 @@ fn testfunc ()
             test "test" 1 2 3 (a b c) (: 10 true) (: d e) 3.5 (: (1 2 3) (4 5 6))
     #print
         'tostring expr
-    let tab = (expr as Table)
+    let tab = (expr as Cell)
     #print
-        'get tab (Table.new 1 2 3)
+        'get tab (Cell.new 1 2 3)
     ;
 
     let expr =

          
@@ 139,20 139,20 @@ do
 
     print ('kind (UAtom))
 
-    let table = (UAtom (Table))
+    let table = (UAtom (Cell))
     print table
     print
-        Table.new "this" "is" "a" "test"
+        Cell.new "this" "is" "a" "test"
             x = 1
             y = 2.5
             z = 3
     print
         'tostring
             UAtom
-                'set-index (table as Table) 10 (UAtom "test")
+                'set-index (table as Cell) 10 (UAtom "test")
     print
         'get
-            'set (table as Table) 10 "test"
+            'set (table as Cell) 10 "test"
             10
 
     print