Rebol3 Code Examplex


Display an outline as a nested table

Render a hierarchical outline as an HTML (or similar) table where each indentation level becomes a nested table cell.

Rebol [
    title: "Rosetta code: Display an outline as a nested table"
    file:  %Display_an_outline_as_a_nested_table.r3
    url:   https://rosettacode.org/wiki/Display_an_outline_as_a_nested_table
]

indent-to-tree: function [
    "Converts indented text to a nested block tree"
    text [string!]
    /tab tab-size [integer!] "Indentation size (default: 4)"
][
    tab-size: any [tab-size 4]
    lines: split-lines text
    stack: reduce [result: copy []]
    depths: copy [0]
    prev: 0

    foreach line lines [
        indent: tab-size
        parse line [
            any [#" " (++ indent) | #"^-" (indent: indent + tab-size)]
            copy name: to end
        ]
        if empty? name [continue]
        level: indent // tab-size
        if any [
            not zero? indent % tab-size  ;; catches non-multiple indentation
            1 < (level - prev)           ;; catches skipped levels
        ][
            do make error! "Invalid indentation!"
        ]
        prev: level
        ;; pop stack to current level
        while [level < length? depths] [
            take/last depths
            take/last stack
        ]

        node: reduce [name copy []]
        append last stack node
        append/only stack node/2
        append depths level
    ]
    result
]

tree-to-wikitable: function/with [
    "Render a [name children] tree as a WikiTable with colspan"
    tree [block!] "Flat [name children] block from indent-to-tree"
    /html         "Emit HTML table instead of wiki markup"
][
    root-name: tree/1
    children:  tree/2

    max-depth:   subtree-depth children
    total-width: leaf-width children

    ;; one block per depth level, each holds [colspan style name] triples
    rows: array/initial max-depth []

    ;; assign a stable color per top-level child branch
    branch: 0
    foreach [name child] children [
        branch: branch + 1
        style: any [pick palette branch + 1  last palette]
        append rows/1 reduce [leaf-width child  style  name]
        either empty? child [
            ;; leaf at root level — pad all deeper rows with empty cells
            repeat r max-depth - 1 [
                append rows/(1 + r) [1 _ _]
            ]
        ][
            collect-rows child 2 max-depth rows style
        ]
    ]

    ;; -- emit ---------------------------------------------------------------
    out: copy ""

    either html [
        append out {<table border="1" style="text-align:center;">^/}
        ;; root spans all columns
        append out ajoin [
            {<tr><td colspan="} total-width
            {" style="background:} palette/1 {">}
            root-name {</td></tr>^/}
        ]
        repeat d max-depth [
            append out "<tr>"
            foreach [span style name] rows/:d [
                append out either name [
                    ajoin [
                        {^/<td style="background:} style {;"}
                        if span > 1 [ajoin [{ colspan="} span {"}]]
                        {>} name {</td>}
                    ]
                ][ "<td></td>" ]
            ]
            append out "</tr>^/"
        ]
        append out "</table>^/"
    ][
        append out {^{| class="wikitable" style="text-align: center;"^/}
        ;; root spans all columns
        append out ajoin [
            {|-^/| style="background: } palette/1
            {;" colspan=} total-width " | " root-name "^/"
        ]
        repeat d max-depth [
            append out "|-^/"
            foreach [span style name] rows/:d [
                append out either name [
                    ajoin [
                        {| style="background: } style {;" }
                        if span > 1 [ajoin ["colspan=" span " "]]
                        "| " name "^/"
                    ]
                ][ "|  | ^/" ]
            ]
        ]
        append out "|}^/"
    ]
    out
][
    ;; -- helpers and palette live in the function's own context --------------

    leaf-width: function [
        "Number of leaves under a node; a leaf counts as 1"
        children [block!]
    ][
        if empty? children [return 1]
        w: 0
        foreach [name child] children [w: w + leaf-width child]
        w
    ]

    subtree-depth: function [
        "Max depth of subtree; a leaf returns 0, its parent 1, etc."
        children [block!]
    ][
        if empty? children [return 0]
        d: 0
        foreach [name child] children [d: max d subtree-depth child]
        d + 1
    ]

    ;; pastel palette — index 1 = root, index 2+ cycle over branches
    palette: [
        "#ffffe6" "#ffebd2" "#f0fff0" "#e6ffff" "#ffe6ff" "#e6e6ff"
    ]

    collect-rows: function [
        "Walk tree depth-first, depositing [colspan style name] triples into rows"
        children  [block!]
        depth     [integer!]
        max-depth [integer!]
        rows      [block!]
        style     [string!]  "Inherited color from parent branch"
    ][
        foreach [name child] children [
            repend rows/:depth [leaf-width child  style  name]
            either empty? child [
                ;; leaf — pad every deeper row with a blank cell
                repeat r max-depth - depth [
                    append rows/(depth + r) [1 _ _]
                ]
            ][
                collect-rows child depth + 1 max-depth rows style
            ]
        ]
    ]
]

tree: indent-to-tree {Display an outline as a nested table.
    Parse the outline to a tree,
        measuring the indent of each line,
        translating the indentation to a nested structure,
        and padding the tree to even depth.
    count the leaves descending from each node,
        defining the width of a leaf as 1,
        and the width of a parent node as a sum.
            (The sum of the widths of its children)
    and write out a table with 'colspan' values
        either as a wiki table,
        or as HTML.}

print tree-to-wikitable tree
print tree-to-wikitable/html tree

Output:

{| class="wikitable" style="text-align: center;"
|-
| style="background: #ffffe6;" colspan=7 | Display an outline as a nested table.
|-
| style="background: #ffebd2;" colspan=3 | Parse the outline to a tree,
| style="background: #f0fff0;" colspan=2 | count the leaves descending from each node,
| style="background: #e6ffff;" colspan=2 | and write out a table with 'colspan' values
|-
| style="background: #ffebd2;" | measuring the indent of each line,
| style="background: #ffebd2;" | translating the indentation to a nested structure,
| style="background: #ffebd2;" | and padding the tree to even depth.
| style="background: #f0fff0;" | defining the width of a leaf as 1,
| style="background: #f0fff0;" | and the width of a parent node as a sum.
| style="background: #e6ffff;" | either as a wiki table,
| style="background: #e6ffff;" | or as HTML.
|-
|  | 
|  | 
|  | 
|  | 
| style="background: #f0fff0;" | (The sum of the widths of its children)
|  | 
|  | 
|}

<table border="1" style="text-align:center;">
<tr><td colspan="7" style="background:#ffffe6">Display an outline as a nested table.</td></tr>
<tr>
<td style="background:#ffebd2;" colspan="3">Parse the outline to a tree,</td>
<td style="background:#f0fff0;" colspan="2">count the leaves descending from each node,</td>
<td style="background:#e6ffff;" colspan="2">and write out a table with 'colspan' values</td></tr>
<tr>
<td style="background:#ffebd2;">measuring the indent of each line,</td>
<td style="background:#ffebd2;">translating the indentation to a nested structure,</td>
<td style="background:#ffebd2;">and padding the tree to even depth.</td>
<td style="background:#f0fff0;">defining the width of a leaf as 1,</td>
<td style="background:#f0fff0;">and the width of a parent node as a sum.</td>
<td style="background:#e6ffff;">either as a wiki table,</td>
<td style="background:#e6ffff;">or as HTML.</td></tr>
<tr><td></td><td></td><td></td><td></td>
<td style="background:#f0fff0;">(The sum of the widths of its children)</td><td></td><td></td></tr>
</table>