Search code examples
rebolrebol3rebol2

Getting a reference to an enclosing list/block in Rebol


Given a sublist is there a way to get a reference to it's parent/enclosing list/block? For example:

fs: [
 usr [
  local [
   bin []
  ]
  share []
 ]
 bin []
]

l: fs/usr/local ;grab a sublist
p: none ;now grab l's parent (which is fs/usr) without hardcoding the path

Thanks for any help!


Solution

  • There is no "parent" reference for a block! series, as it can be referenced from multiple other block! series, so the concept of "parent" is meaningless in such cases.

    However, you can manually add a back-reference in your block when you need it (just watch out when PROBE-ing or MOLD-ing the "child" block then, it will result in dereferencing pointed blocks leading to a much more verbose result than you want).

    Tip: I sometimes do add back-references for block values using an "hidden" header (just moving the block offset passed the values you want to hide). It's PROBE-resistant and handy, but can't be serialized (e.g. on disk) without having to write your own specific serialization routine to handle those back-references appropriately (means usually replacing them with a non-series value: integer, word, issue, tag...whatever allows you to rebuild that link once loaded back).

    For example:

    >> x: [1 2 3]
    == [1 2 3]
    >> y: [a b c]
    == [a b c]
    >> insert/only x y            ; insert a reference to y in x
    == [1 2 3]
    >> x
    == [[a b c] 1 2 3]            ; y reference is at first position
    >> x: next x                  ; let's hide it now
    == [1 2 3]
    >> x
    == [1 2 3]
    >> x/-1                       ; here is how to retrieve our "hidden" header
    == [a b c]
    

    Please note that /-1 shortcut is currently not working in R3 as in R2.

    So, for your own code example above, you need to make a function that will dive into your initial block structure and insert those hidden back-references, so you can access them easily when needed. Here is an example code for processing your structure:

    insert-back-ref: func [blk [block!] parent [block! none!]][
        if parent [                     ;-- no processing if root call
            insert/only blk next head parent ;-- insert the back-reference in child
            parent/1: blk: next blk     ;-- reset the parent reference to child to
        ]                               ;-- point after the back-ref (and hide it)
        forall blk [
            if block? blk/1 [              ;-- if a child block is found
                insert-back-ref blk/1 blk  ;-- process its content recursively              
            ]
        ]
    ]
    
    insert-back-ref fs none            ;-- pass none as root parent
    
    ?? fs
    l: fs/usr/local
    ?? l
    p: l/-1
    ?? p
    p: p/-1
    ?? p
    

    This will output:

    fs: [
        usr [
            local [
                bin []
            ]
            share []
        ]
        bin []
    ]
    l: [
        bin []
    ]
    p: [
        local [
            bin []
        ]
        share []
    ]
    p: [[
            local [
                bin []
            ]
            share []
        ]
        bin []
    ]
    

    Actually, we have created a graph structure using nested block references that can be navigated very simply using built-in series actions and paths.