Data Structures
- Node Size = 12 bytes
- Node List Size = 256 nodes/page x 12 B/node = 3072 B/page
- Page List Size = 512 page slots x 11 B/slot = 5632 bytes
- Bottom level Node List (or Page) has 256 Nodes
- Page List or Chapter has 512 slots, each slot points to a Page
- Slot = 4-byte ptr. + 2 x 24-bit ptrs. + updated byte
- 4-byte ptr. points to node list (page) = null when page is swapped out
- 2 x 24-bit ptrs. point to next/prev. pages in linked list
- Chapter List Size = 2048 page lists x 4 B/page list = 8192 bytes
- Address Space = 32 (4-byte ptr.) + 4 (16 bytes/node) = 36 bits = 64 GB
- Page Addr: 4-bit book no + 11-bit chapter no + 9-bit page idx = 24 bits
- Addr Value: 24-bit page addr + 8-bit node idx = 32 bits
- Node Types:
- Object Ref Node: 0 bit, 31-bit refcount, 0 bit, 31-bit root (attr) ptr., code ptr.
- Object Value Node: 0 bit, 31-bit refcount, 1 bit, header, 4-byte value
- Lisp Node: 1 bit, 31-bit refcount, 2 x 4-byte ptrs. to lisp, obj ref, obj val nodes
- Tree Node: 2 x 16-bit hdrs., 2 x 4-byte values
- Stack Node: 4-byte hdr., 4-byte value, 4-byte next
- Base Node: prior base ptr., root (parm/loc) ptr., next ptr.
- Long Node: 64-bit long, double, or bitarray
- Callback Node: obj ref, code ptr.
- String Node: 3 x 4-byte Unicode chars.
- ByteZero Node: 12 bytes, null-terminated
- Bytes Node: 12 bytes
- Array Node: same as stack node, used for string, bytezero, bytes, bitarray values
- String List: bytezero value, may contain newline chars.
- Dict Leaf Node: string list, array node (list of values)
- Header Values:
- node, boolean, int, long, float, double, object, lisp, string, bytezero, bytes, bitarray, array, dict, dict leaf, callback, op, paren, null
- Binary Tree: has root, max path size = 32 bits
- object node
- base node
- array, dict
- Class Inheritance:
- Code ptr. may point to method in ancestor class
- Current class includes all ancestor attributes
- Page Types:
- Book (up to 16 of these), Chapter, Page
- Also called chapter list, page list, node list, respectively
- Swap File:
- 16 chapter lists (2048 ptrs. x 4 B/ptr.) = 128K
- 16 x 2048 page lists (512 page slots x 11 B/slot) < 192 MB
- 16 x 2048 x 512 node lists x 3072 B/page = 48 GB
- Tree-balancing functionality needed
- Little or no support for arrays
- Linked list implemented in library using Seq class:
- Data structure: lisp nodes, each node points to current, rest of list
- Properties: first/last lisp nodes, count
- Methods: pop, push, append, insert, delete, getnode, getnext, getprior
- StackSeq class: top, count, pop, push
- Reference counting used for garbage collection
- PageUpdate(currIdx)
- // move pageList[curridx] to end of occupied page list
- if prev(currIdx) = 0 then firstFull = next(currIdx)
- else next(prev(currIdx)) = next(currIdx)
- if next(currIdx) = 0 then lastFull = prev(currIdx)
- else prev(next(currIdx)) = prev(currIdx)
- // append currIdx to occupied page list
- next(currIdx) = prev(currIdx) = 0
- if lastFull = 0 then firstFull = currIdx
- else
- next(lastFull) = currIdx
- prev(currIdx) = lastFull
- lastFull = currIdx
- updated(curridx) = 1
- return
- PageSwapNew(newIdx)
- Occurs when adding a new node list, forcing an old node list to be swapped out
- // newIdx = addr. of new page
- // free page list in RAM is empty
- oldIdx = firstFull // head of occupied page list
- if updated(oldIdx) then write old node list to swap file
- newIdx = addr. of new page
- currIdx = oldIdx
- PageAppend(curridx, newIdx)
- updated(currIdx) = 1
- return
- NullPointer(currIdx)
- Occurs when null ptr. (swapped page) encountered in page list
- // currIdx = idx of swapped page in page list
- // pageList[currIdx] is null
- if next(currIdx) = 0 and firstEmpty = currIdx then PageSwap(curridx)
- else PageRefresh(curridx)
- return
- PageRefresh(curridx)
- Read node list (of currIdx) from swap file
- newIdx = addr. of page just read
- PageAppend(curridx, newIdx)
- return
- PageSwap(curridx)
- // free page list in RAM is empty
- oldIdx = firstFull // head of occupied page list
- if updated(oldIdx) then write old node list to swap file
- Read node list (of currIdx) from swap file
- Overwrite old node list with data read (addr = oldIdx)
- PageAppend(curridx, oldIdx)
- return
- PageAppend(currIdx, newIdx)
- pageList[currIdx] = newIdx
- // remove currIdx from empty page list
- if prev(currIdx) = 0 then firstEmpty = next(currIdx)
- else next(prev(currIdx)) = next(currIdx)
- if next(currIdx) = 0 then lastEmpty = prev(currIdx)
- else prev(next(currIdx)) = prev(currIdx)
- // append currIdx to occupied page list
- next(currIdx) = prev(currIdx) = 0
- if lastFull = 0 then firstFull = currIdx
- else
- next(lastFull) = currIdx
- prev(currIdx) = lastFull
- lastFull = currIdx
- updated(currIdx) = 0
Code Execution
All Jispotalk source code is in Polish notation, in which operators precede their operands. The following algorithm is used, in which operators are stored in one stack and operands in a separate stack. Executable code consists of tree nodes.
rightp = root while true do if rightp = 0 then op = pop operator if op = root then return true if op = while/for/loopbody then pop rightp from operator stack continue if op = if then pop rightp from operator stack pop ( continue if op = block then pop ( pop if from operator stack pop ( pop rightp from operator stack continue count = 0 while true do pop operand if open parenthesis then break push operand on operator stack increment count if op = call then rightp = handlecall(count) continue if op = constructor then rightp = handlecons(count) continue if op = callback then rightp = handlecallback(count) continue pop operand from operator stack push operand repeat count - 1 times pop operand from operator stack push operand rightpop = pop leftpop = pop push op(leftpop, rightpop) // (: obj attridx) => obj... if count = 1 then if unary op then push op(pop) else rightpop = pop leftpop = pop push op(leftpop, rightpop) rightp = pop operator continue currnode = getnode(rightp) if open parenthesis then push on operand stack push rightp on operator stack rightp = currnode.downp else if operand then push on operand stack rightp = currnode.rightp else if operator then push on operator stack rightp = currnode.rightp else if funcbody then handlebody rightp = currnode.rightp else if endfunc then pop downto begin from operator stack pop rightp from operator stack else if while/for then rightp = currnode.rightp push rightp, while/for on operator stack else if do then flag = pop if not flag then pop while, rightp from operator stack pop rightp from operator stack pop ( else if continue then pop downto while from operator stack pop rightp from operator stack else if break then pop downto while from operator stack pop rightp, rightp from operator stack pop ( else if breakfor then pop downto for from operator stack pop rightp, rightp from operator stack pop ( pop ( else if contfor then pop downto loopbody from operator stack pop rightp from operator stack else if then then flag = pop if flag then rightp = currnode.rightp else pop if from operator stack pop ( pop rightp from operator stack else return false pop downto x from operator stack: pop multiple from operator stack if: pop ( while: pop ( do block while flag: while true do block if not flag then break handlecons(count): pop classref from operator stack gen objref: root 0/1 = instance/class vars push objref on operator stack return handlecall(count) handlecall(count): pop objref from operator stack push objref pop codept from operator stack return handlecodept(codept, count) handlecodept(codept, count): repeat count - 2 times pop val from operator stack push val push count - 1 return codept handlecallback(count): pop callback from operator stack unpack objref, codept push objref return handlecodept(codept, count) handlebody: count = pop root = new node for i = count - 2 downto 0 do parm = pop add parm to 1st half of tree[i] objref = parm rightp = currnode.rightp loccount = currnode value repeat loccount times add null node to 2nd half of tree rightp = currnode.rightp