Display an outline as a nested table: Difference between revisions

New post.
m (→‎{{header|zkl}}: Fix comment: Perl 6 --> Raku)
(New post.)
(32 intermediate revisions by 10 users not shown)
Line 1:
{{draft task}}
 
{| class="wikitable" style="text-align: center;"
Line 80:
Display your nested table on this page.
<br>
 
=={{header|AutoHotkey}}==
<syntaxhighlight lang="autohotkey">outline2table(db, Delim:= "`t"){
oNum:=[], oMID:=[], oNod := [], oKid := [], oPnt := [], oMbr := [], oLvl := []
oCrl := ["#ffffe6;", "#ffebd2;", "#f0fff0;", "#e6ffff;", "#ffeeff;"]
col := 0, out := "", anc := ""
; create numerical index for each line
for i, line in StrSplit(db, "`n", "`r")
{
RegExMatch(line, "^(\t*)(.*)$", m)
out .= m1 . i "`n"
oNum[i] := m2
}
db := Trim(out, "`n")
 
; create list of members, parents, kids and their ancestors
for i, mbr in StrSplit(db, "`n", "`r")
{
lvl := 1
While (SubStr(mbr, 1, 1) = Delim)
lvl++, mbr := SubStr(mbr, 2)
if (pLvl >= lvl) && pMbr
col++
, oMbr[pLvl, pMbr] .= "col:" col ",anc:" anc
, oKid[pLvl, pMbr] .= "col:" col ",anc:" anc
if (pLvl > lvl) && pMbr
loop % pLvl - lvl
anc := RegExReplace(anc, "\d+_?$")
if (pLvl < lvl) && pMbr
anc .= pMbr "_"
, oMbr[pLvl, pMbr] .= "col:" col+1 ",anc:" anc
, oPnt[pLvl, pMbr] .= "col:" col+1 ",anc:" anc
pLvl := lvl
pMbr := mbr
;~ oMID[lvl] := TV_Add(mbr, oMID[lvl-1], "Expand")
}
; last one on the list
col++
oMbr[pLvl, pMbr] .= "col:" col ",anc:" anc
oKid[pLvl, pMbr] .= "col:" col ",anc:" anc
 
; setup node color
clr := 1
for lvl, obj in oMbr
for node, str in obj
if (lvl <= 2)
oNod[node, "clr"] := clr++
else
oNod[node, "clr"] := oNod[StrSplit(str, "_").2, "clr"]
; setup node level/column/width
for lvl, obj in oKid
for node, str in obj
{
x := StrSplit(str, ",")
col := StrReplace(x.1, "col:")
anc := Trim(StrReplace(x.2, "anc:"), "_")
for j, a in StrSplit(anc, "_")
oNod[a, "wid"] := (oNod[a, "wid"]?oNod[a, "wid"]:0) + 1
oNod[node, "lvl"] := lvl
oNod[node, "col"] := col
oNod[node, "wid"] := 1
}
for lvl, obj in oPnt
for node, str in obj
{
x := StrSplit(str, ",")
col := StrReplace(x.1, "col:")
anc := Trim(StrReplace(x.2, "anc:"), "_")
oNod[node, "lvl"] := lvl
oNod[node, "col"] := col
}
; setup members by level
for node, obj in oNod
oLvl[obj["lvl"], node] := 1
maxW := 0
for node in oLvl[1]
maxW += oNod[node, "wid"]
; setup HTML
html := "<table class=""wikitable"" style=""text-align: center;"">`n"
for lvl, obj in oLvl
{
pCol := 1
html .= "<tr>`n"
for node, bool in obj
{
while (oNod[node, "col"] <> pCol)
pCol++, html .= "`t<td style=""background: #F9F9F9;""></td>`n"
pCol += oNod[node, "wid"]
if !cNum := Mod(oNod[node, "clr"], 5)
cNum := 5
html .= "`t<td style=""background: " oCrl[cNum] """ colspan=""" oNod[node, "wid"] """>" oNum[node] "</td>`n"
}
while (pCOl <= maxW)
pCol++, html .= "`t<td style=""background: #F9F9F9;""></td>`n"
html .= "</tr>`n"
}
html .= "</table>"
; setup wikitable
wTable := "{| class=""wikitable"" style=""text-align: center;""`n"
for lvl, obj in oLvl
{
pCol := 1
wTable .= "|-`n"
for node, bool in obj
{
while (oNod[node, "col"] <> pCol)
pCol++, wTable .= "| | `n"
pCol += oNod[node, "wid"]
if !cNum := Mod(oNod[node, "clr"], 5)
cNum := 5
wTable .= "| style=""background: " oCrl[cNum] """ colspan=""" oNod[node, "wid"] " |" oNum[node] "`n"
}
while (pCOl <= maxW)
pCol++, wTable .= "| | `n"
}
wTable .= "|}`n"
return [html, wTable]
}
</syntaxhighlight>
Examples:<syntaxhighlight lang="autohotkey">db =
(
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.
)
 
Gui, add, ActiveX, vDocument w1000 r14, HTMLFile
result := outline2table(db)
Document.Write(result.1)
Gui, Show
MsgBox % "HTML:`n" result.1 "`n`nWikitable:`n" result.2
return</syntaxhighlight>
{{out}}
HTML:
<table class="wikitable" style="text-align: center;">
<tr>
<td style="background: #ffffe6;" colspan="7">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;" colspan="1">measuring the indent of each line,</td>
<td style="background: #ffebd2;" colspan="1">translating the indentation to a nested structure,</td>
<td style="background: #ffebd2;" colspan="1">and padding the tree to even depth.</td>
<td style="background: #f0fff0;" colspan="1">defining the width of a leaf as 1,</td>
<td style="background: #f0fff0;" colspan="1">and the width of a parent node as a sum.</td>
<td style="background: #e6ffff;" colspan="1">either as a wiki table,</td>
<td style="background: #e6ffff;" colspan="1">or as HTML.</td>
</tr>
<tr>
<td style="background: #F9F9F9;"></td>
<td style="background: #F9F9F9;"></td>
<td style="background: #F9F9F9;"></td>
<td style="background: #F9F9F9;"></td>
<td style="background: #f0fff0;" colspan="1">(The sum of the widths of its children)</td>
<td style="background: #F9F9F9;"></td>
<td style="background: #F9F9F9;"></td>
</tr>
</table>
 
Wikitable:
{| 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;" colspan="1 |measuring the indent of each line,
| style="background: #ffebd2;" colspan="1 |translating the indentation to a nested structure,
| style="background: #ffebd2;" colspan="1 |and padding the tree to even depth.
| style="background: #f0fff0;" colspan="1 |defining the width of a leaf as 1,
| style="background: #f0fff0;" colspan="1 |and the width of a parent node as a sum.
| style="background: #e6ffff;" colspan="1 |either as a wiki table,
| style="background: #e6ffff;" colspan="1 |or as HTML.
|-
| |
| |
| |
| |
| style="background: #f0fff0;" colspan="1 |(The sum of the widths of its children)
| |
| |
|}
 
=={{header|Go}}==
<langsyntaxhighlight lang="go">package main
 
import (
Line 235 ⟶ 445:
toNest(iNodes2, 0, 0, &n2)
fmt.Println(toMarkup(n2, cols2, 4))
}</langsyntaxhighlight>
 
{{out}}
Line 288 ⟶ 498:
| style="background: #ffebd2; " colspan=1 | (The sum of the widths of its children)
| style="background: #ffebd2; " colspan=1 | Propagating the sums upward as necessary.
| |
| |
|}
 
=={{header|Haskell}}==
<syntaxhighlight lang="haskell">{-# LANGUAGE TupleSections #-}
 
module OutlineTree where
 
import Data.Bifunctor (first)
import Data.Bool (bool)
import Data.Char (isSpace)
import Data.List (find, intercalate)
import Data.Tree (Tree (..), foldTree, levels)
 
---------------- NESTED TABLES FROM OUTLINE --------------
 
wikiTablesFromOutline :: [String] -> String -> String
wikiTablesFromOutline colorSwatch outline =
intercalate "\n\n" $
wikiTableFromTree colorSwatch
<$> ( forestFromLineIndents
. indentLevelsFromLines
. lines
)
outline
 
wikiTableFromTree :: [String] -> Tree String -> String
wikiTableFromTree colorSwatch =
wikiTableFromRows
. levels
. paintedTree colorSwatch
. widthLabelledTree
. (paddedTree "" <*> treeDepth)
 
--------------------------- TEST -------------------------
main :: IO ()
main =
( putStrLn
. wikiTablesFromOutline
[ "#ffffe6",
"#ffebd2",
"#f0fff0",
"#e6ffff",
"#ffeeff"
]
)
"Display an outline as a nested table.\n\
\ Parse the outline to a tree,\n\
\ measuring the indent of each line,\n\
\ translating the indentation to a nested structure,\n\
\ and padding the tree to even depth.\n\
\ count the leaves descending from each node,\n\
\ defining the width of a leaf as 1,\n\
\ and the width of a parent node as a sum.\n\
\ (The sum of the widths of its children)\n\
\ and write out a table with 'colspan' values\n\
\ either as a wiki table,\n\
\ or as HTML."
 
------------- TREE STRUCTURE FROM NESTED TEXT ------------
 
forestFromLineIndents :: [(Int, String)] -> [Tree String]
forestFromLineIndents = go
where
go [] = []
go ((n, s) : xs) =
let (subOutline, rest) = span ((n <) . fst) xs
in Node s (go subOutline) : go rest
 
indentLevelsFromLines :: [String] -> [(Int, String)]
indentLevelsFromLines xs =
let pairs = first length . span isSpace <$> xs
indentUnit = maybe 1 fst (find ((0 <) . fst) pairs)
in first (`div` indentUnit) <$> pairs
 
---------------- TREE PADDED TO EVEN DEPTH ---------------
 
paddedTree :: a -> Tree a -> Int -> Tree a
paddedTree padValue = go
where
go tree n
| 1 >= n = tree
| otherwise =
Node
(rootLabel tree)
( (`go` pred n)
<$> bool nest [Node padValue []] (null nest)
)
where
nest = subForest tree
 
treeDepth :: Tree a -> Int
treeDepth = foldTree go
where
go _ [] = 1
go _ xs = (succ . maximum) xs
 
----------------- SUBTREE WIDTHS MEASURED ----------------
 
widthLabelledTree :: Tree a -> Tree (a, Int)
widthLabelledTree = foldTree go
where
go x [] = Node (x, 1) []
go x xs =
Node
(x, foldr ((+) . snd . rootLabel) 0 xs)
xs
 
------------------- COLOR SWATCH APPLIED -----------------
 
paintedTree :: [String] -> Tree a -> Tree (String, a)
paintedTree [] tree = fmap ("",) tree
paintedTree (color : colors) tree =
Node
(color, rootLabel tree)
( zipWith
(fmap . (,))
(cycle colors)
(subForest tree)
)
 
-------------------- WIKITABLE RENDERED ------------------
 
wikiTableFromRows :: [[(String, (String, Int))]] -> String
wikiTableFromRows rows =
let wikiRow = unlines . fmap cellText
cellText (color, (txt, width))
| null txt = "| |"
| otherwise =
"| "
<> cw color width
<> "| "
<> txt
cw color width =
let go w
| 1 < w = " colspan=" <> show w
| otherwise = ""
in "style=\"background:"
<> color
<> "; \""
<> go width
<> " "
in "{| class=\"wikitable\" "
<> "style=\"text-align: center;\"\n|-\n"
<> intercalate "|-\n" (wikiRow <$> rows)
<> "|}"</syntaxhighlight>
{{Out}}
{| 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)
| |
| |
|}
 
=={{header|J}}==
 
Implementation:
 
<syntaxhighlight lang="j">depth=: (i.~ ~.)@(0 i."1~' '=];._2)
tree=: (i: 0>.<:@{:)\
width=: {{NB. y is tree
c=. *i.#y NB. children
NB. sum of children, inductively
y (+//. c&*)`(~.@[)`]}^:_ c
}}
 
NB. avoid dark colors
NB. avoid dark colors
NB. avoid dark colors
pastel=: {{256#.192+?y$,:3#64}}
 
task=: {{
depths=: depth y NB. outline structure
t=: tree depths NB. outline as tree
pad=: (i.#depths) -. t,I.(=>./)depths
tr=: t,pad NB. outline as constant depth tree
dr=: depths,1+pad{depths
lines=:(#dr){.<@dlb;._2 y
widths=. width tr NB. column widths
top=. I.2>dr
color=.<"1 hfd 8421504 (I.tr e.pad)} (top top} tr)&{^:_ (<:2^24),pastel<:#dr
r=.'{| class="wikitable" style="text-align: center;"',LF
for_d.~.dr do. NB. descend through the depths
k=.I.d=dr NB. all lines at this depth
p=. |:({:,~{&tr)^:d ,:k
j=. k/:p NB. order padding to fit parents
r=. r,'|-',LF
r=. r,;'| style="background: #',L:0 (j{color),L:0'" colspan=',L:0(j{widths),&":each' | ',L:0 (j{lines),L:0 LF
end.
r=.r,'|}',LF
}}</syntaxhighlight>
 
Given the task example outline:
 
<syntaxhighlight lang="j">outline=:{{)n
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.
}}</syntaxhighlight>
 
Generated output from <tt>task outline</tt> was:
 
{| class="wikitable" style="text-align: center;"
|-
| style="background: #ffffff" colspan=7 | Display an outline as a nested table.
|-
| style="background: #e7d7cd" colspan=3 | Parse the outline to a tree,
| style="background: #e9c5d1" colspan=2 | count the leaves descending from each node,
| style="background: #d8e9f4" colspan=2 | and write out a table with 'colspan' values
|-
| style="background: #e7d7cd" colspan=1 | measuring the indent of each line,
| style="background: #e7d7cd" colspan=1 | translating the indentation to a nested structure,
| style="background: #e7d7cd" colspan=1 | and padding the tree to even depth.
| style="background: #e9c5d1" colspan=1 | defining the width of a leaf as 1,
| style="background: #e9c5d1" colspan=1 | and the width of a parent node as a sum.
| style="background: #d8e9f4" colspan=1 | either as a wiki table,
| style="background: #d8e9f4" colspan=1 | or as HTML.
|-
| style="background: #808080" colspan=1 |
| style="background: #808080" colspan=1 |
| style="background: #808080" colspan=1 |
| style="background: #808080" colspan=1 |
| style="background: #e9c5d1" colspan=1 | (The sum of the widths of its children)
| style="background: #808080" colspan=1 |
| style="background: #808080" colspan=1 |
|}
 
=={{header|Java}}==
<syntaxhighlight lang="java">
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
 
public final class DisplayAnOutlineAsANestedTable {
 
public static void main(String[] args) {
String outline = """
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)
Propagating the sums upward as necessary.
and write out a table with 'colspan' values
either as a wiki table,
or as HTML.
Optionally add color to the nodes.
""";
Node tree = parse(outline);
colourTree(tree);
String htmlCode = htmlTable(tree);
System.out.println(htmlCode);
String wikiCode = wikiTable(tree);
System.out.println(wikiCode);
}
// Return the HTML code for the display of the given Node as a table.
private static String htmlTable(Node tree) {
final int tableColumnCount = tree.colspan();
 
int rowColumn = 0;
StringBuilder builder = new StringBuilder("<table style='text-align: center;' >\n");
 
// Breadth first traversal of 'tree'.
Deque<Node> queue = new ArrayDeque<Node>();
Set<Node> explored = new HashSet<Node>();
queue.offer(tree);
while ( ! queue.isEmpty() ) {
Node currentNode = queue.poll();
if ( explored.contains(currentNode) ) {
continue;
}
if ( rowColumn == 0 ) {
builder.append(" <tr>\n");
}
 
builder.append(htmlTableData(currentNode));
rowColumn += currentNode.colspan();
 
if ( rowColumn == tableColumnCount ) {
builder.append(" </tr>\n");
rowColumn = 0;
}
for ( Node child : currentNode.children ) {
queue.offer(child);
}
explored.add(currentNode);
}
 
builder.append("</table>\n");
return builder.toString();
}
// Return the code for the display of the given Node as a table in Wikipedia.
private static String wikiTable(Node tree) {
final int tableColumnCount = tree.colspan();
int rowColumn = 0;
 
StringBuilder builder = new StringBuilder();
builder.append("{| class=\"" + "wikitable" + "\"" + " style=\"" + "text-align: center;" + "\"" + "\n");
 
// Breadth first traversal of 'tree'.
Deque<Node> queue = new ArrayDeque<Node>();
Set<Node> explored = new HashSet<Node>();
queue.offer(tree);
while ( ! queue.isEmpty() ) {
Node currentNode = queue.poll();
if ( explored.contains(currentNode) ) {
continue;
}
if ( rowColumn == 0 ) {
builder.append("|-\n");
}
builder.append(wikiTableData(currentNode));
rowColumn += currentNode.colspan();
if ( rowColumn == tableColumnCount ) {
rowColumn = 0;
}
for ( Node child : currentNode.children ) {
queue.offer(child);
}
explored.add(currentNode);
}
 
builder.append("|}\n");
return builder.toString();
}
// Return an HTML table data element for the given Node.
private static String htmlTableData(Node node) {
String indent = " ";
String colspan = " colspan=\"" + node.colspan() + "\"";
String style = "style=\"" + "background-color: " + node.colour + "\"";
String attributes = colspan + " " + style;
return indent + "<td" + attributes + " >" + node.text + "</td>\n";
}
// Return a Wikipedia table data element for the given Node.
private static String wikiTableData(Node node) {
if ( node.text.isBlank() ) {
return "| |\n";
}
 
String style = "style=\"" + "background: " + node.colour + " \"";
String colspan = " colspan=" + node.colspan();
String attributes = style + colspan;
return "| " + attributes + " | " + node.text + "\n";
}
// Return the given outline as a tree of Node.
private static Node parse(String outline) {
List<Token> tokens = tokenise(outline);
Node temporaryTree = new Node("", -1, null);
parse(tokens, 0, temporaryTree);
Node tree = temporaryTree.children.getFirst();
padTree(tree, tree.height());
return tree;
}
// Recursively build a tree of Node.
private static void parse(List<Token> tokens, int index, Node node) {
if ( index == tokens.size() ) {
return;
}
 
Token token = tokens.get(index);
if ( token.indent == node.indent ) { // A sibling of node
Node current = new Node(token.text, token.indent, node.parent);
node.parent.children.addLast(current);
parse(tokens, index + 1, current);
} else if ( token.indent > node.indent ) { // A child of node
Node current = new Node(token.text, token.indent, node);
node.children.addLast(current);
parse(tokens, index + 1, current);
} else if ( token.indent < node.indent ) { // Try the node's parent until a sibling is found
parse(tokens, index, node.parent);
}
}
// Pad the tree with blank nodes so that all branches have the same depth.
private static void padTree(Node node, int height) {
if ( node.isLeaf() && node.depth() < height ) {
Node padNode = new Node("", node.indent + 1, node);
node.children.addLast(padNode);
}
 
for ( Node child : node.children ) {
padTree(child, height);
}
}
private static void colourTree(Node node) {
if ( node.text.isBlank() ) {
node.colour = Colour.blank();
} else if ( node.depth() <= 1 ) {
node.colour = Colour.next();
} else {
node.colour = node.parent.colour;
}
 
for ( Node child : node.children ) {
colourTree(child);
}
}
private static List<Token> tokenise(String outline) {
List<Token> tokens = new ArrayList<Token>();
for ( String line : outline.split("\n") ) {
String lineTrimmed = line.trim();
final int indent = line.length() - lineTrimmed.length();
tokens.addLast( new Token(lineTrimmed, indent) );
}
 
return tokens;
}
private static final class Node {
public Node (String aText, int aIndent, Node aParent) {
text = aText;
indent = aIndent;
parent = aParent;
children = new ArrayList<Node>();
}
public int depth() {
return ( parent != null ) ? parent.depth() + 1 : -1;
}
 
public int height() {
if ( isLeaf() ) {
return 0;
}
return children.stream().map( child -> child.height() ).max(Comparator.naturalOrder()).get() + 1;
}
 
public int colspan() {
if ( isLeaf() ) {
return 1;
}
 
return children.stream().map( child -> child.colspan() ).mapToInt(Integer::intValue).sum();
}
 
public boolean isLeaf() {
return children.isEmpty();
}
private String text;
private int indent;
private Node parent;
private List<Node> children;
private String colour;
}
private static final class Colour {
public static String next() {
index = ( index + 1 ) % colours.size();
return colours.get(index);
}
public static String blank() {
return "#cccccc;";
}
private static int index = -1;
private static final List<String> colours = List.of( "#ffff66;", "#ffcc66;", "#ccffcc;", "#ccccff;",
"#ffcccc;", "#00cccc;", "#cc9966;", "#ffccff;" );
}
 
private record Token(String text, int indent) { }
 
}
</syntaxhighlight>
{{ out }}
HTML Table
 
<table style='text-align: center;' >
<tr>
<td colspan="9" style="background-color: #ffff66;" >Display an outline as a nested table.</td>
</tr>
<tr>
<td colspan="3" style="background-color: #ffcc66;" >Parse the outline to a tree,</td>
<td colspan="3" style="background-color: #ccffcc;" >count the leaves descending from each node,</td>
<td colspan="2" style="background-color: #ccccff;" >and write out a table with 'colspan' values</td>
<td colspan="1" style="background-color: #ffcccc;" >Optionally add color to the nodes.</td>
</tr>
<tr>
<td colspan="1" style="background-color: #ffcc66;" >measuring the indent of each line,</td>
<td colspan="1" style="background-color: #ffcc66;" >translating the indentation to a nested structure,</td>
<td colspan="1" style="background-color: #ffcc66;" >and padding the tree to even depth.</td>
<td colspan="1" style="background-color: #ccffcc;" >defining the width of a leaf as 1,</td>
<td colspan="2" style="background-color: #ccffcc;" >and the width of a parent node as a sum.</td>
<td colspan="1" style="background-color: #ccccff;" >either as a wiki table,</td>
<td colspan="1" style="background-color: #ccccff;" >or as HTML.</td>
<td colspan="1" style="background-color: #cccccc;" ></td>
</tr>
<tr>
<td colspan="1" style="background-color: #cccccc;" ></td>
<td colspan="1" style="background-color: #cccccc;" ></td>
<td colspan="1" style="background-color: #cccccc;" ></td>
<td colspan="1" style="background-color: #cccccc;" ></td>
<td colspan="1" style="background-color: #ccffcc;" >(The sum of the widths of its children)</td>
<td colspan="1" style="background-color: #ccffcc;" >Propagating the sums upward as necessary.</td>
<td colspan="1" style="background-color: #cccccc;" ></td>
<td colspan="1" style="background-color: #cccccc;" ></td>
<td colspan="1" style="background-color: #cccccc;" ></td>
</tr>
</table>
 
 
Wiki Table
 
{| class="wikitable" style="text-align: center;"
|-
| style="background: #ffff66; " colspan=9 | Display an outline as a nested table.
|-
| style="background: #ffcc66; " colspan=3 | Parse the outline to a tree,
| style="background: #ccffcc; " colspan=3 | count the leaves descending from each node,
| style="background: #ccccff; " colspan=2 | and write out a table with 'colspan' values
| style="background: #ffcccc; " colspan=1 | Optionally add color to the nodes.
|-
| style="background: #ffcc66; " colspan=1 | measuring the indent of each line,
| style="background: #ffcc66; " colspan=1 | translating the indentation to a nested structure,
| style="background: #ffcc66; " colspan=1 | and padding the tree to even depth.
| style="background: #ccffcc; " colspan=1 | defining the width of a leaf as 1,
| style="background: #ccffcc; " colspan=2 | and the width of a parent node as a sum.
| style="background: #ccccff; " colspan=1 | either as a wiki table,
| style="background: #ccccff; " colspan=1 | or as HTML.
| |
|-
| |
| |
| |
| |
| style="background: #ccffcc; " colspan=1 | (The sum of the widths of its children)
| style="background: #ccffcc; " colspan=1 | Propagating the sums upward as necessary.
| |
| |
| |
Line 293 ⟶ 1,102:
 
=={{header|JavaScript}}==
<langsyntaxhighlight lang="javascript">(() => {
'"use strict'";
 
// ----------- NESTED TABLES FROM OUTLINE ------------
 
// wikiTablesFromOutline :: [String] -> String -> String
const wikiTablesFromOutline = colorSwatch =>
outline => forestFromIndentedLines(
indentLevelsFromLines(lines(outline))
)
.map(wikiTableFromTree(colorSwatch))
.join("\n\n");
 
 
// wikiTableFromTree :: [String] -> Tree String -> String
const wikiTableFromTree = colorSwatch =>
compose(
wikiTableFromRows,
levels,
paintedTree(colorSwatch),
widthLabelledTree,
ap(paddedTree(""))(treeDepth)
);
 
// ---------------------- TEST -----------------------
// main :: IO ()
const main = () => {
Line 309 ⟶ 1,140:
and write out a table with 'colspan' values
either as a wiki table,
or as HTML.`;
 
console.logreturn wikiTablesFromOutline([
wikiTableFromForest("#ffffe6",
forestOfEvenDepth("#ffebd2",
map(compose("#f0fff0",
"#e6ffff",
coloredKeyLines(Object.keys(dictColors)),
paintedTree('backColor')('yellow'),"#ffeeff"
measuredTree])(outline);
))(
forestFromOutline(outline)
)
)
)
);
};
 
// TRANSLATION OF OUTLINE TO NESTED TABLE --------- TREE STRUCTURE FROM NESTED TEXT ---------
 
// forestFromOutlineforestFromIndentedLines :: [(Int, String)] -> [Tree String]
// [Tree String]
const forestFromOutline = s =>
const forestFromIndentedLines = tuples => {
// A list of trees, derived from an
//const indentedgo outline= text.xs =>
forestFromLineIndents 0 < xs.length ? (() => {
indentLevelsFromLines(lines(s)) // First line and its sub-tree,
const [indented, body] = Array.from(
);
xs[0]
),
[tree, rest] = Array.from(
span(compose(lt(indented), fst))(
tail(xs)
)
);
 
// followed by the rest.
// wikiTableFromForest :: [Tree (String, Dict)] -> String
return [
const wikiTableFromForest = forest => {
// Lines of wiki markup representing a nested Node(body)(go(tree))
// with 'colspan' values for parent nodes, ].concat(go(rest));
// and varying background colors.})() : [];
 
const tableRows = trees => {
return go(tuples);
const rows = tail(levels(Node('virtual')(trees)));
return unlines(concatMap(row =>
cons('|-')(
map(cell => {
const
dct = cell[1],
color = dct.backColor,
width = dct.leafSum;
return '| ' + (
Boolean(color) ? (
'style="background: ' +
dictColors[color] + '; "'
) : ''
) + (
1 < width ? (
' colspan=' + str(width)
) : ''
) + ' | ' + cell[0];
})(row)
)
)(rows));
};
return '{| class="wikitable" style="text-align: center;"\n' +
tableRows(forest) +
'\n|}'
};
 
 
// indentLevelsFromLines :: [String] -> [(Int, String)]
const indentLevelsFromLines = xs => {
// A list of (indentLevel, trimmed Text) tuples.
const
indentTextPairspairs = xs.map(compose(
firstArrowx => bimap(length),(cs span=> cs.join(isSpace""))(
span(isSpace)(list(x)),
indentUnit = minimum(indentTextPairs.flatMap(pair => {)
const w = fst(pair);,
indentUnit = return 0 < w ? [w] : [];pairs.reduce(
} (a, tpl)); => {
return indentTextPairs.map( const i = tpl[0];
 
firstArrow(flip(div)(indentUnit))
); return 0 < i ? (
i < a ? i : a
) : a;
},
Infinity
);
 
return [Infinity, 0].includes(indentUnit) ? (
pairs
) : pairs.map(first(n => n / indentUnit));
};
 
// ------------ TREE PADDED TO EVEN DEPTH ------------
// forestFromLineIndents :: [(Int, String)] -> [Tree String]
 
const forestFromLineIndents = tuples => {
// paddedTree :: a //-> ATree lista of-> nestedInt trees-> derivedTree froma
const paddedTree = padValue =>
// a list of indented lines.
const// goAll =descendants xsexpanded =>to same depth
// with empty nodes 0where < xsneeded.length ? (() => {
node => const [n, s]depth => Array.from(xs[0]);{
const go = n //=> Linestree indented under this line,=>
//1 tupled< withn all? the(() rest.=> {
const [firstTreeLines, rest] const children = Array.fromnest(tree);
 
span(x => n < x[0])(xs.slice(1))
return Node(root(tree))(
(
0 < children.length ? (
children
) : [Node(padValue)([])]
).map(go(n - 1))
);
})() : tree;
 
return go(depth)(node);
};
 
// treeDepth :: Tree a -> Int
const treeDepth = tree =>
foldTree(
() => xs => 0 < xs.length ? (
1 + maximum(xs)
) : 1
)(tree);
 
// ------------- SUBTREE WIDTHS MEASURED -------------
 
// widthLabelledTree :: Tree a -> Tree (a, Int)
const widthLabelledTree = tree =>
// A tree in which each node is labelled with
// the width of its own subtree.
foldTree(x => xs =>
0 < xs.length ? (
Node(Tuple(x)(
xs.reduce(
(a, node) => a + snd(root(node)),
0
)
))(xs)
) : Node(Tuple(x)(1))([])
)(tree);
 
// -------------- COLOR SWATCH APPLIED ---------------
 
// paintedTree :: [String] -> Tree a -> Tree (String, a)
const paintedTree = colorSwatch =>
tree => 0 < colorSwatch.length ? (
Node(
Tuple(colorSwatch[0])(root(tree))
)(
zipWith(compose(fmapTree, Tuple))(
cycle(colorSwatch.slice(1))
)(
nest(tree)
)
)
) : fmapTree(Tuple(""))(tree);
 
// --------------- WIKITABLE RENDERED ----------------
 
// wikiTableFromRows ::
// [[(String, (String, Int))]] -> String
const wikiTableFromRows = rows => {
const
cw = color => width => {
const go = w =>
1 < w ? (
`colspan=${w} `
) : "";
 
return `style="background:${color}; "` + (
` ${go(width)}`
);
// This first tree}, and then the rest.
cellText = ctw => return [Node(s)(go(firstTreeLines))]{
const [color, tw] = Array.concatfrom(go(rest)ctw);
})() : const [txt, width] = Array.from(tw);
 
return go(tuples);
return 0 < txt.length ? (
`| ${cw(color)(width)}| ${txt}`
) : "| |";
},
classText = "class=\"wikitable\"",
styleText = "style=\"text-align:center;\"",
header = `{| ${classText} ${styleText}\n|-`,
tableBody = rows.map(
cells => cells.map(cellText).join("\n")
).join("\n|-\n");
 
return `${header}\n${tableBody}\n|}`;
};
 
// ------------------ GENERIC TREES ------------------
// forestOfEvenDepth :: [Tree (a, Dict)] -> [Tree (a, Dict)]
 
const forestOfEvenDepth = measuredTrees => {
// ANode :: treea padded-> downwards[Tree soa] that-> everyTree brancha
const Node = v =>
// descends to the same depth.
const// goConstructor =for na =>Tree treenode =>which connects a
// value of some 1kind >=to na ?list (of zero or
// more child treetrees.
xs => ) : Node(tree.root)({
type: 0 < tree.nest.length ? ("Node",
root: tree.nest.map(go(n - 1))v,
nest: xs || ) : [Node(Tuple('')({}))([])]
});
 
return measuredTrees.map(go(
 
1 + maximumBy(x => root(x)[1].layerSum)(
// fmapTree :: (a -> b) -> Tree a -> Tree measuredTreesb
const fmapTree = f => {
).root[1].layerSum
));// A new tree. The result of a
// structure-preserving application of f
// to each root in the existing tree.
const go = t => Node(
f(t.root)
)(
t.nest.map(go)
);
 
return go;
};
 
// BACKGROUND COLOURS FOR SECTIONS OF THE TREE ----
 
// coloredKeyLinesfoldTree :: (a -> [Stringb] -> b) -> Tree a -> b
const foldTree = f => {
// Tree (String, Dict) -> Tree (String, Dict)
// The catamorphism on trees. A summary
const coloredKeyLines = colorNames => node =>
// value obtained by a depth-first fold.
Node(root(node))(
const go = tree zipWith(paintedTree('backColor'))=> f(
takeroot(node.nest.lengthtree)(
cycle)(tail(colorNames))
nest(tree).map(go)
)(node.nest)
);
 
return go;
// paintedTree :: String -> a -> Tree (b, dict) -> Tree (b, dict)
const paintedTree = k => v => node => {
const go = x =>
Node(Tuple(root(x)[0])(
insertDict(k)(v)(root(x)[1])
))(nest(x).map(go));
return go(node);
};
 
 
// dictColors :: Dict
// levels :: Tree a -> [[a]]
const dictColors = {
const levels = tree yellow:=> '#ffffe6',{
// A list of lists, grouping the root
orange: '#ffebd2',
// values of each level of the tree.
green: '#f0fff0',
blue:const '#e6ffff'go = (a, node) => {
const [h, ...t] = 0 < a.length ? (
pink: '#ffeeff',
gray: '' a
) : [
[],
[]
];
 
return [
[node.root, ...h],
...node.nest.slice(0)
.reverse()
.reduce(go, t)
];
};
 
return go([], tree);
};
 
 
// nest :: Tree a -> [a]
// GENERIC FUNCTIONS ----------------------------
const nest = tree => {
// Allowing for lazy (on-demand) evaluation.
// If the nest turns out to be a function –
// rather than a list – that function is applied
// here to the root, and returns a list.
const xs = tree.nest;
 
// Node :: a ->return [Tree"function" a]!== ->typeof Treexs a? (
const Node = v => xs => ({ xs
type) : 'Node',xs(root(tree));
};
root: v, // any type of value (consistent across tree)
 
nest: xs || []
 
// root :: Tree a -> a
const root = tree =>
// The value attached to a tree node.
tree.root;
 
// --------------------- GENERIC ---------------------
 
// Just :: a -> Maybe a
const Just = x => ({
type: "Maybe",
Nothing: false,
Just: x
});
 
 
// Tuple (,) :: a -> b -> (a, b)
const// TupleNothing =:: Maybe a => b => ({
const Nothing = () type:=> 'Tuple',({
'0'type: a"Maybe",
'1'Nothing: b,true
length: 2
});
 
 
// Tuple (,) :: a -> b -> (a, b)
const Tuple = a =>
b => ({
type: "Tuple",
"0": a,
"1": b,
length: 2
});
 
 
// apFn :: (a -> b -> c) -> (a -> b) -> (a -> c)
const ap = f =>
// Applicative instance for functions.
// f(x) applied to g(x).
g => x => f(x)(
g(x)
);
 
 
// bimap :: (a -> b) -> (c -> d) -> (a, c) -> (b, d)
const bimap = f =>
// Tuple instance of bimap.
// A tuple of the application of f and g to the
// first and second values respectively.
g => tpl => Tuple(f(tpl[0]))(
g(tpl[1])
);
 
 
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
const compose = (...fs) =>
x// =>A fs.reduceRight((a,function f)defined =>by f(a),the x);right-to-left
// composition of all the functions in fs.
fs.reduce(
(f, g) => x => f(g(x)),
x => x
);
 
// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => xs =>
xs.flatMap(f);
 
// cons :: a -> [a] -> [a]
const cons = x => xs =>
Array.isArray(xs) ? (
[x].concat(xs)
) : 'GeneratorFunction' !== xs.constructor.constructor.name ? (
x + xs
) : ( // Existing generator wrapped with one additional element
function*() {
yield x;
let nxt = xs.next()
while (!nxt.done) {
yield nxt.value;
nxt = xs.next();
}
}
)();
 
// cycle :: [a] -> Generator [a]
const cycle = function* cycle(xs) {
// An infinite repetition of xs,
// from which an arbitrary prefix
// may be taken.
const lng = xs.length;
let i = 0;
 
while (true) {
yield( xs[i]);
i = (1 + i) % lng;
}
};
 
// div :: Int -> Int -> Int
const div = x => y => Math.floor(x / y);
 
// Liftfirst :: (a simple-> functionb) to-> one((a, whichc) applies-> to a tuple(b, c))
// transforming only theconst first item of= thef tuple=>
 
// firstArrow :: (a -> b) -> ((a, c) -> (b, c))
const firstArrow = f =>
// A simple function lifted to one which applies
// to a tuple, transforming only its first item.
xy => Tuple(f(xy[0]))({
const tpl = Tuple(f(xy[0]))(xy[1]);
);
 
// flip :: (a -> b -> c) ->return bArray.isArray(xy) ->? a -> c(
Array.from(tpl)
const flip = f =>
1 < f.length ? () : tpl;
(a, b) => f(b, a)};
) : (x => y => f(y)(x));
 
// foldTree :: (a -> [b] -> b) -> Tree a -> b
const foldTree = f => tree => {
const go = node => f(node.root)(
node.nest.map(go)
);
return go(tree);
};
 
// foldl1 :: (a -> a -> a) -> [a] -> a
const foldl1 = f => xs =>
1 < xs.length ? xs.slice(1)
.reduce(uncurry(f), xs[0]) : xs[0];
 
// fromEnum :: Enum a => a -> Int
const fromEnum = x =>
typeof x !== 'string' ? (
x.constructor === Object ? (
x.value
) : parseInt(Number(x))
) : x.codePointAt(0);
 
// fst :: (a, b) -> a
const fst = tpl => tpl[0];
// First member of a pair.
tpl[0];
 
// gt :: Ord a => a -> a -> Bool
const gt = x => y =>
'Tuple' === x.type ? (
x[0] > y[0]
) : (x > y);
 
// insertDict :: String -> a -> Dict -> Dict
const insertDict = k => v => dct =>
Object.assign({}, dct, {
[k]: v
});
 
// isSpace :: Char -> Bool
const isSpace = c => /\s/.test(c);
// True if c is a white space character.
(/\s/u).test(c);
 
// Returns Infinity over objects without finite length.
// This enables zip and zipWith to choose the shorter
// argument when one is non-finite, like cycle, repeat etc
 
// length :: [a] -> Int
const length = xs =>
// Returns Infinity over objects without finite
(Array.isArray(xs) || 'string' === typeof xs) ? (
// length. This enables zip and zipWith to choose
// the shorter argument when one is non-finite,
// like cycle, repeat etc
"GeneratorFunction" !== xs.constructor
.constructor.name ? (
xs.length
) : Infinity;
 
// levels :: Tree a -> [[a]]
const levels = tree => {
const xs = [
[root(tree)]
];
let level = [tree].flatMap(nest);
while (0 < level.length) {
xs.push(level.map(root));
level = level.flatMap(nest);
}
return xs;
};
 
// lines :: String -> [String]
const lines = s => s.split(/[\r\n]/);
// A list of strings derived from a single
// string delimited by newline and or CR.
0 < s.length ? (
s.split(/[\r\n]+/u)
) : [];
 
 
// map :: (a -> b) -> [a] -> [b]
const// maplist =:: fStringOrArrayLike b => xsb =-> [a]
const list = (Array.isArray(xs) ? (=>
// xs itself, if it is an Array,
// or an Array derived from xs.
Array.isArray(xs) ? (
xs
) : xsArray.splitfrom('')).map(fxs || []);
 
// max :: Ord a => a -> a -> a
const max = a => b => gt(b)(a) ? b : a;
 
// maximumBylt (<) :: (Ord a -=> a -> Ordering) -> [a] -> aBool
const maximumBylt = f => xsa =>
0b <=> xs.lengtha ?< (b;
xs.slice(1)
.reduce((a, x) => 0 < f(x)(a) ? x : a, xs[0])
) : undefined;
 
// measuredTree :: Tree a -> Tree (a, (Int, Int, Int))
const measuredTree = tree => {
// A tree in which each node is tupled with
// a (leafSum, layerSum, nodeSum) measure of its sub-tree,
// where leafSum is the number of descendant leaves,
// and layerSum is the number of descendant levels,
// and nodeSum counts all nodes, including the root.
// Index is a position in a zero-based top-down
// left to right series.
// For additional parent indices, see parentIndexedTree.
const whni = (w, h, n, i) => ({
leafSum: w,
layerSum: h,
nodeSum: n,
index: i
});
let i = 0;
return foldTree(
x => {
let topDown = i++;
return xs => Node(
Tuple(x)(
0 < xs.length ? (() => {
const dct = xs.reduce(
(a, node) => {
const dimns = node.root[1];
return whni(
a.leafSum + dimns.leafSum,
max(a.layerSum)(
dimns.layerSum
),
a.nodeSum + dimns.nodeSum,
topDown
);
}, whni(0, 0, 0, topDown)
);
return whni(
dct.leafSum,
1 + dct.layerSum,
1 + dct.nodeSum,
topDown
);
})() : whni(1, 0, 1, topDown)
)
)(xs);
}
)(tree);
};
 
// minimummaximum :: Ord a => [a] -> a
const minimummaximum = xs => (
0// <The xs.lengthlargest ?value (in a non-empty list.
foldl1(ays => x => x0 < ays.length ? x : a)(xs)
) : undefined; ys.slice(1).reduce(
(a, y) => y > a ? (
y
) : a, ys[0]
)
) : undefined
)(list(xs));
 
// nest :: Tree a -> [a]
const nest = tree => tree.nest;
 
// rootsnd :: Tree (a, b) -> ab
const rootsnd = treetpl => tree.root;
// Second member of a pair.
tpl[1];
 
// snd :: (a, b) -> b
const snd = tpl => tpl[1];
 
// span :: (a -> Bool) -> [a] -> ([a], [a])
const span = p => xs => {
const// iLastLongest =prefix of xs.length -consisting of elements 1;which
// all satisfy p, tupled with the remainder of xs.
return splitAt(
until(ixs => iLast < i || !p(xs[i]))({
const i = xs.findIndex(x succ=> !p(x));
)(0)
)(xs);
};
 
// splitAt :: Int -> [a] return ->1 ([a],!== [a])i ? (
const splitAt = n => Tuple(xs.slice(0, =>i))(
Tuple( xs.slice(0, ni))(
xs.slice(n )
) : Tuple(xs)([]);
};
 
// str :: a -> String
const str = x => x.toString();
 
// succ :: Enum a => a -> a
const succ = x => 1 + x;
 
// tail :: [a] -> [a]
const tail = xs => 0 < xs.length ? xs.slice(1) : [];
// A new list consisting of all
// items of xs except the first.
"GeneratorFunction" !== xs.constructor
.constructor.name ? (
(ys => 0 < ys.length ? ys.slice(1) : [])(
xs
)
) : (take(1)(xs), xs);
 
 
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = n => xs =>
// The first n elements of a list,
'GeneratorFunction' !== xs.constructor.constructor.name ? (
// string of characters, or stream.
xs => "GeneratorFunction" !== xs
.constructor.constructor.name ? (
xs.slice(0, n)
) : [].concat.apply([], ...Array.from({
length: n
}, () => {
const x = xs.next();
 
return x.done ? [] : [x.value];
}));
 
// uncurry :: (a -> b -> c) -> ((a, b) -> c)
const uncurry = f =>
function() {
const
args = Array.from(arguments),
a = 1 < args.length ? (
args
) : args[0]; // Tuple object.
return f(a[0])(a[1]);
};
 
// unlinesuncons :: [Stringa] -> StringMaybe (a, [a])
const unlinesuncons = xs => xs.join('\n');{
// Just a tuple of the head of xs and its tail,
// Or Nothing if xs is an empty list.
const lng = length(xs);
 
// until :: (a -> Bool) ->return (a0 ->< alng) ->? a -> a(
const until = p => f => x =Infinity > {lng ? (
let v = x; // Finite list
while (!p(v)) v = f Just(vTuple(xs[0])(xs.slice(1)));
return v; ) : (() => {
// Lazy generator
const nxt = take(1)(xs);
 
return 0 < nxt.length ? (
Just(Tuple(nxt[0])(xs))
) : Nothing();
})()
) : Nothing();
};
 
 
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = f => xs => ys =>
// A list with the length of the shorter of
xs.slice(
// xs and 0ys, Math.min(xs.length,defined ys.length)by zipping with a
).map((x// custom function, i)rather than =>with f(x)(ys[i]));the
// default tuple constructor.
xs => ys => {
const n = Math.min(length(xs), length(ys));
 
return Infinity > n ? (
(([as, bs]) => Array.from({
length: n
}, (_, i) => f(as[i])(
bs[i]
)))([xs, ys].map(
take(n)
))
) : zipWithGen(f)(xs)(ys);
};
 
 
// zipWithGen :: (a -> b -> c) ->
// Gen [a] -> Gen [b] -> Gen [c]
const zipWithGen = f => ga => gb => {
const go = function* (ma, mb) {
let
a = ma,
b = mb;
 
while (!a.Nothing && !b.Nothing) {
const
ta = a.Just,
tb = b.Just;
 
yield f(fst(ta))(fst(tb));
a = uncons(snd(ta));
b = uncons(snd(tb));
}
};
 
return go(uncons(ga), uncons(gb));
};
 
// MAIN ---
return main();
})();</langsyntaxhighlight>
{{Out}}
{| 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)
| |
| |
|}
 
=={{header|Julia}}==
<langsyntaxhighlight lang="julia">using DataFrames
 
text = """
Line 844 ⟶ 1,752:
textplus = text * " Optionally add color to the nodes."
htmlfromdataframe(processtable(textplus))
</langsyntaxhighlight>{{out}}
<h4>A Rosetta Code Nested Table</h4><table style="width:100%" class="wikitable" >
<tr>
Line 916 ⟶ 1,824:
style = "background: #ddffdd;" >(The sum of the widths of its children)</td>
</tr>
</table>
 
=={{header|Mathematica}} / {{header|Wolfram Language}}==
<syntaxhighlight lang="mathematica">s = "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.";
s = StringSplit[s, "\n"];
indentation = LengthWhile[Characters[#], EqualTo[" "]] & /@ s;
s = MapThread[StringDrop, {s, indentation}];
indentation =
indentation /.
Thread[Union[indentation] -> Range[Length[Union[indentation]]]];
ii = Transpose[{Range[Length[indentation]], indentation}];
(*ii//Grid*)
 
sel = Table[
{i, Last@Select[ii, #[[2]] < i[[2]] \[And] #[[1]] < i[[1]] &]}
,
{i, Rest@ii}
];
g = Graph[Rule @@@ sel[[All, All, 1]], VertexLabels -> "Name"];
 
vl = VertexList[g];
head = FirstPosition[vl, 1][[1]];
dm = GraphDistanceMatrix[g];
depth = ReverseSortBy[Transpose[{vl, dm[[All, head]]}], Last];
colspandb = <||>;
data = Table[
vert = d[[1]];
vd = VertexInDegree[g, vert];
vics = VertexInComponent[g, vert, {1}];
vocs = Rest@VertexOutComponent[g, vert];
cspan = 0;
Do[
If[KeyExistsQ[colspandb, vic],
cspan += colspandb[vic]
]
,
{vic, vics}
];
If[cspan == 0, cspan = 1];
AssociateTo[colspandb, d[[1]] -> cspan];
{Sequence @@ d, vd, vics, vocs, cspan}
,
{d, depth}
];
 
emptybefore = Table[
{d[[1]],
Length@
Select[
data, #[[1]] < d[[1]] \[And]
Length[#[[4]]] == 0 \[And] #[[2]] < d[[2]] &][[All, {1, 2,
3}]]}
,
{d, data}
];
emptybefore = Association[Rule @@@ emptybefore];
 
depthcopy = depth;
depthcopy[[All, 2]] += 1;
graphelements =
SortBy[Sort /@ GatherBy[depthcopy, Last], First /* Last][[All, All,
1]];
 
str = {"<table style='text-align: center;'>"};
colorsdb = <|1 -> "#ffffe6", 2 -> "#ffebd2", 6 -> "#f0fff0",
10 -> "#e6ffff"|>;
Do[
AppendTo[str, "<tr>"];
totalspan = 0;
Do[
If[KeyExistsQ[colorsdb, g],
color = colorsdb[g]
,
(*Print["sel",SelectFirst[data,First/*EqualTo[g]][[5]]];*)
color =
colorsdb[
Max[
Intersection[SelectFirst[data, First /* EqualTo[g]][[5]],
Keys[colorsdb]]]]
];
span = SelectFirst[data, First /* EqualTo[g]][[6]];
totalspan += span;
empty = emptybefore[g];
str = str~Join~
ConstantArray["<td style=\"background-color: #F9F9F9;\"></td>",
empty];
If[span == 1,
AppendTo[str,
"<td style=\"background-color: " <> color <> ";\">" <> s[[g]] <>
"</td>"];
,
AppendTo[str,
"<tdcolspan=\"" <> ToString[span] <>
"\" style=\"background-color: " <> color <> ";\">" <> s[[g]] <>
"</td>"];
];
,
{g, ge}
];
extra =
SelectFirst[data, First /* EqualTo[1]][[6]] - totalspan - empty;
str = str~Join~
ConstantArray["<td style=\"background-color: #F9F9F9;\"></td>",
extra];
AppendTo[str, "</tr>"];
,
{ge, graphelements}
]
AppendTo[str, "</table>"];
StringRiffle[str, "\n"]</syntaxhighlight>
{{out}}
<pre><table style='text-align: center;'>
<tr>
<tdcolspan="7" style="background-color: #ffffe6;">Display an outline as a nested table.</td>
</tr>
<tr>
<tdcolspan="3" style="background-color: #ffebd2;">Parse the outline to a tree,</td>
<tdcolspan="2" style="background-color: #f0fff0;">count the leaves descending from each node,</td>
<tdcolspan="2" style="background-color: #e6ffff;">and write out a table with 'colspan' values</td>
</tr>
<tr>
<td style="background-color: #ffebd2;">measuring the indent of each line,</td>
<td style="background-color: #ffebd2;">translating the indentation to a nested structure,</td>
<td style="background-color: #ffebd2;">and padding the tree to even depth.</td>
<td style="background-color: #f0fff0;">defining the width of a leaf as 1,</td>
<td style="background-color: #f0fff0;">and the width of a parent node as a sum.</td>
<td style="background-color: #e6ffff;">either as a wiki table,</td>
<td style="background-color: #e6ffff;">or as HTML.</td>
</tr>
<tr>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #f0fff0;">(The sum of the widths of its children)</td>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #F9F9F9;"></td>
</tr>
</table></pre>
 
=={{header|Nim}}==
 
<syntaxhighlight lang="nim">import strutils
 
const Outline = """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."""
 
type Color {.pure.} = enum
NoColor
Yellow = "#ffffe6;"
Orange = "#ffebd2;"
Green = "#f0fff0;"
Blue = "#e6ffff;"
 
const Line1Color = Yellow
const Line2Colors = [Orange, Green, Blue]
 
type Node = ref object
value: string
level: Natural
width: Natural
color: Color
parent: Node
children: seq[Node]
 
#---------------------------------------------------------------------------------------------------
 
proc leadingSpaces(line: string): int =
## return the number of leading spaces.
while line[result] == ' ':
inc result
 
#---------------------------------------------------------------------------------------------------
 
proc buildTree(outline: string): tuple[root: Node, depth: Natural] =
## Build the tree for the given outline.
 
result.root = Node()
var level: int
var startPos = @[-1]
var nodes: seq[Node] = @[result.root]
var linecount = 0
 
for line in Outline.splitLines:
inc linecount
if line.len == 0: continue
let start = line.leadingSpaces()
level = startPos.find(start)
 
if level < 0:
# Level not yet encountered.
if start < startPos[^1]:
raise newException(ValueError, "wrong indentation at line " & $linecount)
startPos.add(start)
nodes.add(nil)
level = startPos.high
 
# Create the node.
let node = Node(value: line.strip(), level: level)
let parent = nodes[level - 1]
parent.children.add(node)
node.parent = parent
nodes[level] = node # Set the node as current node for this level.
 
result.depth = nodes.high
 
#---------------------------------------------------------------------------------------------------
 
proc padTree(node: Node; depth: Natural) =
## pad the tree with empty nodes to get an even depth.
if node.level == depth:
return
if node.children.len == 0:
# Add an empty node.
node.children.add(Node(level: node.level + 1, parent: node))
for child in node.children:
child.padTree(depth)
 
#---------------------------------------------------------------------------------------------------
 
proc computeWidths(node: Node) =
## Compute the widths.
var width = 0
if node.children.len == 0:
width = 1
else:
for child in node.children:
child.computeWidths()
inc width, child.width
node.width = width
 
#---------------------------------------------------------------------------------------------------
 
proc build(nodelists: var seq[seq[Node]]; node: Node) =
## Build the list of nodes per level.
nodelists[node.level].add(node)
for child in node.children:
nodelists.build(child)
 
#---------------------------------------------------------------------------------------------------
 
proc setColors(nodelists: seq[seq[Node]]) =
## Set the colors of the nodes.
for node in nodelists[1]:
node.color = Line1Color
for i, node in nodelists[2]:
node.color = Line2Colors[i mod Line2Colors.len]
for level in 3..nodelists.high:
for node in nodelists[level]:
node.color = if node.value.len != 0: node.parent.color else: NoColor
 
#---------------------------------------------------------------------------------------------------
 
proc writeWikiTable(nodelists: seq[seq[Node]]) =
## Output the wikitable.
echo "{| class='wikitable' style='text-align: center;'"
for level in 1..nodelists.high:
echo "|-"
for node in nodelists[level]:
if node.width > 1:
# Node with children.
echo "| style='background: $1 ' colspan=$2 | $3".format(node.color, node.width, node.value)
elif node.value.len > 0:
# Leaf with contents.
echo "| style='background: $1 ' | $2".format(node.color, node.value)
else:
# Empty cell.
echo "| | "
echo "|}"
 
#---------------------------------------------------------------------------------------------------
 
proc writeHtml(nodelists: seq[seq[Node]]) =
## Output the HTML.
echo "<table class='wikitable' style='text-align: center;'>"
for level in 1..nodelists.high:
echo " <tr>"
for node in nodelists[level]:
if node.width > 1:
# Node with children.
echo " <td colspan='$1' style='background-color: $2'>$3</td>".format(node.width, node.color, node.value)
elif node.value.len > 0:
# Leaf with contents.
echo " <td style='background-color: $1'>$2</td>".format(node.color, node.value)
else:
# Empty cell.
echo " <td></td>"
echo " </tr>"
echo "</table>"
 
#———————————————————————————————————————————————————————————————————————————————————————————————————
 
let (root, depth) = Outline.buildTree()
root.padTree(depth)
root.computeWidths()
var nodelists = newSeq[seq[Node]](depth + 1)
nodelists.build(root)
nodelists.setColors()
echo "WikiTable:"
nodelists.writeWikiTable()
echo "HTML:"
nodelists.writeHtml()</syntaxhighlight>
 
{{out}}
WikiTable:
{| 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)
| |
| |
|}
HTML:
<table class='wikitable' style='text-align: center;'>
<tr>
<td colspan='7' style='background-color: #ffffe6;'>Display an outline as a nested table.</td>
</tr>
<tr>
<td colspan='3' style='background-color: #ffebd2;'>Parse the outline to a tree,</td>
<td colspan='2' style='background-color: #f0fff0;'>count the leaves descending from each node,</td>
<td colspan='2' style='background-color: #e6ffff;'>and write out a table with 'colspan' values</td>
</tr>
<tr>
<td style='background-color: #ffebd2;'>measuring the indent of each line,</td>
<td style='background-color: #ffebd2;'>translating the indentation to a nested structure,</td>
<td style='background-color: #ffebd2;'>and padding the tree to even depth.</td>
<td style='background-color: #f0fff0;'>defining the width of a leaf as 1,</td>
<td style='background-color: #f0fff0;'>and the width of a parent node as a sum.</td>
<td style='background-color: #e6ffff;'>either as a wiki table,</td>
<td style='background-color: #e6ffff;'>or as HTML.</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td style='background-color: #f0fff0;'>(The sum of the widths of its children)</td>
<td></td>
<td></td>
</tr>
</table>
 
=={{header|Perl}}==
<langsyntaxhighlight lang="perl">#!/usr/bin/perl
 
use strict;
Line 973 ⟶ 2,261:
and write out a table with 'colspan' values
either as a wiki table,
or as HTML.</langsyntaxhighlight>
{{out}}
<table border=1 cellspacing=0>
Line 1,006 ⟶ 2,294:
=={{header|Phix}}==
Can output in either html or wikitable markup
<!--<syntaxhighlight lang="phix">(phixonline)-->
<lang Phix>constant html = false,
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
outlines = {"""
<span style="color: #008080;">constant</span> <span style="color: #000000;">html</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span><span style="color: #0000FF;">,</span>
Display an outline as a nested table.
<span style="color: #000000;">outlines</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"""
Parse the outline to a tree,
Display an outline as a nested table.
measuring the indent of each line,
translatingParse the indentationoutline to a nested structuretree,
and paddingmeasuring the treeindent toof eveneach depth.line,
translating the indentation to a nested structure,
count the leaves descending from each node,
defining theand widthpadding ofthe atree leafto aseven 1,depth.
andcount the widthleaves ofdescending afrom parenteach node as a sum.,
defining the (The sumwidth of thea widths ofleaf itsas children)1,
and writethe outwidth of a tableparent withnode 'colspan'as a valuessum.
(The sum of the widths of its children)
either as a wiki table,
and write out ora astable HTML.""", """with 'colspan' values
Display an outline either as a nestedwiki table.,
or as HTML."""</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"""
Parse the outline to a tree,
Display an outline as a nested table.
measuring the indent of each line,
translatingParse the indentationoutline to a nested structuretree,
and paddingmeasuring the treeindent toof even depth.each line,
translating the indentation to a nested structure,
count the leaves descending from each node,
defining theand widthpadding the oftree ato leafeven asdepth. 1,
andcount the widthleaves ofdescending afrom parenteach node as a sum.,
defining the (The sumwidth of thea widths ofleaf itsas children)1,
and the width Propagatingof thea sumsparent upwardnode as necessary.a sum.
(The sum of the widths of its children)
and write out a table with 'colspan' values
Propagating the sums upward as necessary.
either as a wiki table,
and write out ora astable HTML.with 'colspan' values
either as a wiki table,
Optionally add color to the nodes."""}
or as HTML.
 
Optionally add color to the nodes."""</span><span style="color: #0000FF;">}</span>
constant yellow = "#ffffe6;",
orange = "#ffebd2;",
<span style="color: #008080;">constant</span> <span style="color: #000000;">yellow</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"#ffffe6;"</span><span style="color: #0000FF;">,</span>
green = "#f0fff0;",
<span style="color: #000000;">orange</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"#ffebd2;"</span><span style="color: #0000FF;">,</span>
blue = "#e6ffff;",
<span style="color: #000000;">green</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"#f0fff0;"</span><span style="color: #0000FF;">,</span>
pink = "#ffeeff;",
<span style="color: #000000;">blue</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"#e6ffff;"</span><span style="color: #0000FF;">,</span>
colours = {{yellow, orange, green, blue, pink},
<span style="color: #000000;">pink</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"#ffeeff;"</span><span style="color: #0000FF;">,</span>
{blue, yellow, orange, green, pink}}
<span style="color: #000000;">colours</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #000000;">yellow</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">orange</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">green</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">blue</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">pink</span><span style="color: #0000FF;">},</span>
 
<span style="color: #0000FF;">{</span><span style="color: #000000;">blue</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">yellow</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">orange</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">green</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">pink</span><span style="color: #0000FF;">}}</span>
function calc_spans(sequence lines, integer ldx)
sequence children = lines[ldx][$]
<span style="color: #008080;">function</span> <span style="color: #000000;">calc_spans</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">ldx</span><span style="color: #0000FF;">)</span>
if length(children)!=0 then
<span style="color: #004080;">sequence</span> <span style="color: #000000;">children</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">ldx</span><span style="color: #0000FF;">][$]</span>
integer span = 0
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">children</span><span style="color: #0000FF;">)!=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
for i=1 to length(children) do
<span style="color: #004080;">integer</span> <span style="color: #000000;">span</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
integer child = children[i]
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">children</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
lines = calc_spans(lines,child)
<span style="color: #004080;">integer</span> <span style="color: #000000;">child</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">children</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
span += lines[child][4]
<span style="color: #000000;">lines</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">calc_spans</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">,</span><span style="color: #000000;">child</span><span style="color: #0000FF;">)</span>
end for
<span style="color: #000000;">span</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">child</span><span style="color: #0000FF;">][</span><span style="color: #000000;">4</span><span style="color: #0000FF;">]</span>
lines[ldx][4] = span
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
-- else -- (span already 1)
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">ldx</span><span style="color: #0000FF;">][</span><span style="color: #000000;">4</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">span</span>
end if
<span style="color: #000080;font-style:italic;">-- else -- (span already 1)</span>
return lines
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
end function
<span style="color: #008080;">return</span> <span style="color: #000000;">lines</span>
 
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
procedure markup(string outline, sequence colours)
sequence lines = split(outline,"\n",no_empty:=true),
<span style="color: #008080;">procedure</span> <span style="color: #000000;">markup</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">outline</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">sequence</span> <span style="color: #000000;">colours</span><span style="color: #0000FF;">)</span>
pi = {}, -- indents (to locate parents)
<span style="color: #004080;">sequence</span> <span style="color: #000000;">lines</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #000000;">outline</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">no_empty</span><span style="color: #0000FF;">:=</span><span style="color: #004600;">true</span><span style="color: #0000FF;">),</span>
pdx = {}, -- indexes for ""
<span style="color: #000000;">pi</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{},</span> <span style="color: #000080;font-style:italic;">-- indents (to locate parents)</span>
children = {}
<span style="color: #000000;">pdx</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{},</span> <span style="color: #000080;font-style:italic;">-- indexes for ""</span>
string text
<span style="color: #000000;">children</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
integer maxdepth = 0,
<span style="color: #004080;">string</span> <span style="color: #000000;">text</span>
parent, depth, span
<span style="color: #004080;">integer</span> <span style="color: #000000;">maxdepth</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">,</span>
for i=1 to length(lines) do
<span style="color: #000000;">parent</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">depth</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">span</span>
string line = trim_tail(lines[i])
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
text = trim_head(line)
<span style="color: #004080;">string</span> <span style="color: #000000;">line</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">trim_tail</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])</span>
integer indent = length(line)-length(text)
<span style="color: #000000;">text</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">trim_head</span><span style="color: #0000FF;">(</span><span style="color: #000000;">line</span><span style="color: #0000FF;">)</span>
-- remove any completed parents
<span style="color: #004080;">integer</span> <span style="color: #000000;">indent</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">line</span><span style="color: #0000FF;">)-</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">text</span><span style="color: #0000FF;">)</span>
while length(pi) and indent<=pi[$] do
<span style="color: #000080;font-style:italic;">-- remove any completed parents</span>
pi = pi[1..$-1]
<span style="color: #008080;">while</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">and</span> <span style="color: #000000;">indent</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">[$]</span> <span style="color: #008080;">do</span>
pdx = pdx[1..$-1]
<span style="color: #000000;">pi</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">pi</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..$-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
end while
<span style="color: #000000;">pdx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">pdx</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..$-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
parent = 0
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
if length(pi) then
<span style="color: #000000;">parent</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
parent = pdx[$]
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
lines[parent][$] &= i -- (update children)
<span style="color: #000000;">parent</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">pdx</span><span style="color: #0000FF;">[$]</span>
end if
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">][$]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">deep_copy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">][$])</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">i</span> <span style="color: #000080;font-style:italic;">-- (update children)</span>
pi &= indent
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
pdx &= i
<span style="color: #000000;">pi</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">indent</span>
depth = length(pi)
<span style="color: #000000;">pdx</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">i</span>
span = 1 -- (default/assume no children[=={}])
<span style="color: #000000;">depth</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">)</span>
lines[i] = {i,depth,indent,span,parent,text,children}
<span style="color: #000000;">span</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span> <span style="color: #000080;font-style:italic;">-- (default/assume no children[=={}])</span>
maxdepth = max(maxdepth,depth)
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">i</span><span style="color: #0000FF;">,</span><span style="color: #000000;">depth</span><span style="color: #0000FF;">,</span><span style="color: #000000;">indent</span><span style="color: #0000FF;">,</span><span style="color: #000000;">span</span><span style="color: #0000FF;">,</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">,</span><span style="color: #000000;">text</span><span style="color: #0000FF;">,</span><span style="color: #000000;">children</span><span style="color: #0000FF;">}</span>
end for
<span style="color: #000000;">maxdepth</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">max</span><span style="color: #0000FF;">(</span><span style="color: #000000;">maxdepth</span><span style="color: #0000FF;">,</span><span style="color: #000000;">depth</span><span style="color: #0000FF;">)</span>
lines = calc_spans(lines,1)
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
 
<span style="color: #000000;">lines</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">calc_spans</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
string res = iff(html?"<table class=\"wikitable\" style=\"text-align: center;\">\n"
:"{| class=\"wikitable\" style=\"text-align: center;\"\n")
<span style="color: #004080;">string</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">html</span><span style="color: #0000FF;">?</span><span style="color: #008000;">"&lt;table class=\"wikitable\" style=\"text-align: center;\"&gt;\n"</span>
for d=1 to maxdepth do
<span style="color: #0000FF;">:</span><span style="color: #008000;">"{| class=\"wikitable\" style=\"text-align: center;\"\n"</span><span style="color: #0000FF;">)</span>
res &= iff(html?"<tr>\n"
<span style="color: #008080;">for</span> <span style="color: #000000;">d</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">maxdepth</span> <span style="color: #008080;">do</span>
:"|-\n")
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">html</span><span style="color: #0000FF;">?</span><span style="color: #008000;">"&lt;tr&gt;\n"</span>
integer cdx = 1
<span style="color: #0000FF;">:</span><span style="color: #008000;">"|-\n"</span><span style="color: #0000FF;">)</span>
for i=1 to length(lines) do
<span style="color: #004080;">integer</span> <span style="color: #000000;">cdx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">lii</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">lident</span>
{{},depth,{},span,parent,text,children} = lines[i]
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
if depth=2 then cdx += 1 end if
<span style="color: #0000FF;">{</span><span style="color: #000000;">lii</span><span style="color: #0000FF;">,</span><span style="color: #000000;">depth</span><span style="color: #0000FF;">,</span><span style="color: #000000;">lident</span><span style="color: #0000FF;">,</span><span style="color: #000000;">span</span><span style="color: #0000FF;">,</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">,</span><span style="color: #000000;">text</span><span style="color: #0000FF;">,</span><span style="color: #000000;">children</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
string style = sprintf(`style="background: %s"`,{colours[cdx]})
<span style="color: #008080;">if</span> <span style="color: #000000;">depth</span><span style="color: #0000FF;">=</span><span style="color: #000000;">2</span> <span style="color: #008080;">then</span> <span style="color: #000000;">cdx</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
if depth=d then
<span style="color: #004080;">string</span> <span style="color: #000000;">style</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">`style="background: %s"`</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">colours</span><span style="color: #0000FF;">[</span><span style="color: #000000;">cdx</span><span style="color: #0000FF;">]})</span>
if span!=1 then style &= sprintf(` colspan="%d"`,span) end if
<span style="color: #008080;">if</span> <span style="color: #000000;">depth</span><span style="color: #0000FF;">=</span><span style="color: #000000;">d</span> <span style="color: #008080;">then</span>
res &= sprintf(iff(html?"<td %s>%s</td>\n"
<span style="color: #008080;">if</span> <span style="color: #000000;">span</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span> <span style="color: #000000;">style</span> <span style="color: #0000FF;">&=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">` colspan="%d"`</span><span style="color: #0000FF;">,</span><span style="color: #000000;">span</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
:"| %s | %s\n"),{style,text})
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">html</span><span style="color: #0000FF;">?</span><span style="color: #008000;">"&lt;td %s&gt;%s&lt;/td&gt;\n"</span>
elsif depth<d and children={} then
<span style="color: #0000FF;">:</span><span style="color: #008000;">"| %s | %s\n"</span><span style="color: #0000FF;">),{</span><span style="color: #000000;">style</span><span style="color: #0000FF;">,</span><span style="color: #000000;">text</span><span style="color: #0000FF;">})</span>
-- res &= iff(html?"<td></td>\n"
<span style="color: #008080;">elsif</span> <span style="color: #000000;">depth</span><span style="color: #0000FF;"><</span><span style="color: #000000;">d</span> <span style="color: #008080;">and</span> <span style="color: #000000;">children</span><span style="color: #0000FF;">={}</span> <span style="color: #008080;">then</span>
-- :"| |\n")
<span style="color: #000080;font-style:italic;">-- res &= sprintf(iff(html?"<&lt;td %s><&gt;&lt;/td>&gt;\n"
-- :"| %s |\n"),{style})</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">html</span><span style="color: #0000FF;">?</span><span style="color: #008000;">"&lt;td %s&gt;&lt;/td&gt;\n"</span>
end if
<span style="color: #0000FF;">:</span><span style="color: #008000;">"| %s |\n"</span><span style="color: #0000FF;">),{</span><span style="color: #000000;">style</span><span style="color: #0000FF;">})</span>
end for
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
if html then
<span style="color: #008080;">end</span> res<span &style="color: #008080;">for</trspan>\n"
<span style="color: #008080;">if</span> <span style="color: #000000;">html</span> <span style="color: #008080;">then</span>
end if
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">"&lt;/tr&gt;\n"</span>
end for
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
res &= iff(html?"</table>\n"
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
:"|}\n")
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">html</span><span style="color: #0000FF;">?</span><span style="color: #008000;">"&lt;/table&gt;\n"</span>
puts(1,res)
<span style="color: #0000FF;">:</span><span style="color: #008000;">"|}\n"</span><span style="color: #0000FF;">)</span>
end procedure
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">res</span><span style="color: #0000FF;">)</span>
for i=1 to length(outlines) do
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
markup(outlines[i],colours[i])
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">outlines</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
end for</lang>
<span style="color: #000000;">markup</span><span style="color: #0000FF;">(</span><span style="color: #000000;">outlines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #000000;">colours</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<!--</syntaxhighlight>-->
{{out}}
in html:
Line 1,237 ⟶ 2,528:
| style="background: #f0fff0;" |
| style="background: #ffeeff;" |
|}
 
=={{header|Python}}==
===Python: Procedural===
<syntaxhighlight lang="python">"""Display an outline as a nested table. Requires Python >=3.6."""
 
import itertools
import re
import sys
 
from collections import deque
from typing import NamedTuple
 
 
RE_OUTLINE = re.compile(r"^((?: |\t)*)(.+)$", re.M)
 
COLORS = itertools.cycle(
[
"#ffffe6",
"#ffebd2",
"#f0fff0",
"#e6ffff",
"#ffeeff",
]
)
 
 
class Node:
def __init__(self, indent, value, parent, children=None):
self.indent = indent
self.value = value
self.parent = parent
self.children = children or []
 
self.color = None
 
def depth(self):
if self.parent:
return self.parent.depth() + 1
return -1
 
def height(self):
"""Height of the subtree rooted at this node."""
if not self.children:
return 0
return max(child.height() for child in self.children) + 1
 
def colspan(self):
if self.leaf:
return 1
return sum(child.colspan() for child in self.children)
 
@property
def leaf(self):
return not bool(self.children)
 
def __iter__(self):
# Level order tree traversal.
q = deque()
q.append(self)
while q:
node = q.popleft()
yield node
q.extend(node.children)
 
 
class Token(NamedTuple):
indent: int
value: str
 
 
def tokenize(outline):
"""Generate ``Token``s from the given outline."""
for match in RE_OUTLINE.finditer(outline):
indent, value = match.groups()
yield Token(len(indent), value)
 
 
def parse(outline):
"""Return the given outline as a tree of ``Node``s."""
# Split the outline into lines and count the level of indentation.
tokens = list(tokenize(outline))
 
# Parse the tokens into a tree of nodes.
temp_root = Node(-1, "", None)
_parse(tokens, 0, temp_root)
 
# Pad the tree so that all branches have the same depth.
root = temp_root.children[0]
pad_tree(root, root.height())
 
return root
 
 
def _parse(tokens, index, node):
"""Recursively build a tree of nodes.
 
Args:
tokens (list): A collection of ``Token``s.
index (int): Index of the current token.
node (Node): Potential parent or sibling node.
"""
# Base case. No more lines.
if index >= len(tokens):
return
 
token = tokens[index]
 
if token.indent == node.indent:
# A sibling of node
current = Node(token.indent, token.value, node.parent)
node.parent.children.append(current)
_parse(tokens, index + 1, current)
 
elif token.indent > node.indent:
# A child of node
current = Node(token.indent, token.value, node)
node.children.append(current)
_parse(tokens, index + 1, current)
 
elif token.indent < node.indent:
# Try the node's parent until we find a sibling.
_parse(tokens, index, node.parent)
 
 
def pad_tree(node, height):
"""Pad the tree with blank nodes so all branches have the same depth."""
if node.leaf and node.depth() < height:
pad_node = Node(node.indent + 1, "", node)
node.children.append(pad_node)
 
for child in node.children:
pad_tree(child, height)
 
 
def color_tree(node):
"""Walk the tree and color each node as we go."""
if not node.value:
node.color = "#F9F9F9"
elif node.depth() <= 1:
node.color = next(COLORS)
else:
node.color = node.parent.color
 
for child in node.children:
color_tree(child)
 
 
def table_data(node):
"""Return an HTML table data element for the given node."""
indent = " "
 
if node.colspan() > 1:
colspan = f'colspan="{node.colspan()}"'
else:
colspan = ""
 
if node.color:
style = f'style="background-color: {node.color};"'
else:
style = ""
 
attrs = " ".join([colspan, style])
return f"{indent}<td{attrs}>{node.value}</td>"
 
 
def html_table(tree):
"""Return the tree as an HTML table."""
# Number of columns in the table.
table_cols = tree.colspan()
 
# Running count of columns in the current row.
row_cols = 0
 
# HTML buffer
buf = ["<table style='text-align: center;'>"]
 
# Breadth first iteration.
for node in tree:
if row_cols == 0:
buf.append(" <tr>")
 
buf.append(table_data(node))
row_cols += node.colspan()
 
if row_cols == table_cols:
buf.append(" </tr>")
row_cols = 0
 
buf.append("</table>")
return "\n".join(buf)
 
 
def wiki_table_data(node):
"""Return an wiki table data string for the given node."""
if not node.value:
return "| |"
 
if node.colspan() > 1:
colspan = f"colspan={node.colspan()}"
else:
colspan = ""
 
if node.color:
style = f'style="background: {node.color};"'
else:
style = ""
 
attrs = " ".join([colspan, style])
return f"| {attrs} | {node.value}"
 
 
def wiki_table(tree):
"""Return the tree as a wiki table."""
# Number of columns in the table.
table_cols = tree.colspan()
 
# Running count of columns in the current row.
row_cols = 0
 
# HTML buffer
buf = ['{| class="wikitable" style="text-align: center;"']
 
for node in tree:
if row_cols == 0:
buf.append("|-")
 
buf.append(wiki_table_data(node))
row_cols += node.colspan()
 
if row_cols == table_cols:
row_cols = 0
 
buf.append("|}")
return "\n".join(buf)
 
 
def example(table_format="wiki"):
"""Write an example table to stdout in either HTML or Wiki format."""
 
outline = (
"Display an outline as a nested table.\n"
" Parse the outline to a tree,\n"
" measuring the indent of each line,\n"
" translating the indentation to a nested structure,\n"
" and padding the tree to even depth.\n"
" count the leaves descending from each node,\n"
" defining the width of a leaf as 1,\n"
" and the width of a parent node as a sum.\n"
" (The sum of the widths of its children)\n"
" and write out a table with 'colspan' values\n"
" either as a wiki table,\n"
" or as HTML."
)
 
tree = parse(outline)
color_tree(tree)
 
if table_format == "wiki":
print(wiki_table(tree))
else:
print(html_table(tree))
 
 
if __name__ == "__main__":
args = sys.argv[1:]
 
if len(args) == 1:
table_format = args[0]
else:
table_format = "wiki"
 
example(table_format)</syntaxhighlight>
{{out}}
 
Wiki table
 
{| class="wikitable" style="text-align: center;"
|-
| colspan=7 style="background: #ffffe6;" | Display an outline as a nested table.
|-
| colspan=3 style="background: #ffebd2;" | Parse the outline to a tree,
| colspan=2 style="background: #f0fff0;" | count the leaves descending from each node,
| colspan=2 style="background: #e6ffff;" | 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)
| |
| |
|}
 
HTML table
 
<pre><table style='text-align: center;'>
<tr>
<tdcolspan="7" style="background-color: #ffffe6;">Display an outline as a nested table.</td>
</tr>
<tr>
<tdcolspan="3" style="background-color: #ffebd2;">Parse the outline to a tree,</td>
<tdcolspan="2" style="background-color: #f0fff0;">count the leaves descending from each node,</td>
<tdcolspan="2" style="background-color: #e6ffff;">and write out a table with 'colspan' values</td>
</tr>
<tr>
<td style="background-color: #ffebd2;">measuring the indent of each line,</td>
<td style="background-color: #ffebd2;">translating the indentation to a nested structure,</td>
<td style="background-color: #ffebd2;">and padding the tree to even depth.</td>
<td style="background-color: #f0fff0;">defining the width of a leaf as 1,</td>
<td style="background-color: #f0fff0;">and the width of a parent node as a sum.</td>
<td style="background-color: #e6ffff;">either as a wiki table,</td>
<td style="background-color: #e6ffff;">or as HTML.</td>
</tr>
<tr>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #f0fff0;">(The sum of the widths of its children)</td>
<td style="background-color: #F9F9F9;"></td>
<td style="background-color: #F9F9F9;"></td>
</tr>
</table></pre>
 
===Python: Functional===
<syntaxhighlight lang="python">'''Display an outline as a nested table'''
 
from itertools import chain, cycle, takewhile
from functools import reduce
from operator import add
 
 
# wikiTablesFromOutline :: [String] -> String -> String
def wikiTablesFromOutline(colorSwatch):
'''Wikitable markup for (colspan) tables representing
the indentation of a given outline.
Each key-line point (child of a tree root) has a
distinct color, inherited by all its descendants.
The first color in the swatch is for the root node.
A sequence of tables is generated where the outline
represents a forest rather than a singly-rooted tree.
'''
def go(outline):
return '\n\n'.join([
wikiTableFromTree(colorSwatch)(tree) for tree in
forestFromLevels(
indentLevelsFromLines(
outline.splitlines()
)
)
])
return go
 
 
# wikiTableFromTree :: [String] -> Tree String -> String
def wikiTableFromTree(colorSwatch):
'''A wikitable rendered from a single tree.
'''
return compose(
wikiTableFromRows,
levels,
paintedTree(colorSwatch),
widthMeasuredTree,
ap(paddedTree(""))(treeDepth)
)
 
 
# ------------------------- TEST -------------------------
# main :: IO ()
def main():
'''A colored wikitable rendering of a given outline'''
 
outline = '''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(
wikiTablesFromOutline([
"#ffffe6",
"#ffebd2",
"#f0fff0",
"#e6ffff",
"#ffeeff"
])(outline)
)
 
 
# ------------------ TREE FROM OUTLINE -------------------
 
# indentLevelsFromLines :: [String] -> [(Int, String)]
def indentLevelsFromLines(xs):
'''Each input line stripped of leading
white space, and tupled with a preceding integer
giving its level of indentation from 0 upwards.
'''
indentTextPairs = [
(n, s[n:]) for (n, s)
in (
(len(list(takewhile(isSpace, x))), x)
for x in xs
)
]
indentUnit = len(next(
x for x in indentTextPairs if x[0]
)) or 1
return [
(x[0] // indentUnit, x[1])
for x in indentTextPairs
]
 
 
# forestFromLevels :: [(Int, String)] -> [Tree a]
def forestFromLevels(levelValuePairs):
'''A list of trees derived from a list of values paired
with integers giving their levels of indentation.
'''
def go(xs):
if xs:
level, v = xs[0]
children, rest = span(
lambda x: level < x[0]
)(xs[1:])
return [Node(v)(go(children))] + go(rest)
else:
return []
return go(levelValuePairs)
 
 
# -------------- TREE PADDED TO EVEN DEPTH ---------------
 
# paddedTree :: a -> (Int, Node a) -> Node a
def paddedTree(padValue):
'''A tree vertically padded to a given depth,
with additional nodes, containing padValue,
where needed.
'''
def go(tree):
def pad(n):
prev = n - 1
return Node(tree.get('root'))([
go(x)(prev) for x in (
tree.get('nest') or [Node(padValue)([])]
)
]) if prev else tree
return pad
return go
 
 
# treeDepth :: Tree a -> Int
def treeDepth(tree):
'''Maximum number of distinct levels in the tree.
'''
def go(_, xs):
return 1 + max(xs) if xs else 1
return foldTree(go)(tree)
 
 
# ------------ SPANNING WIDTH OF EACH SUBTREE ------------
 
# widthMeasuredTree :: Tree a -> Tree (a, Int)
def widthMeasuredTree(tree):
'''A tree in which each node value is tupled
with the width of the subtree.
'''
def go(x, xs):
return Node((x, 1))([]) if not xs else (
Node((x, reduce(
lambda a, child: a + (
child.get('root')[1]
),
xs,
0
)))(xs)
)
return foldTree(go)(tree)
 
 
# ----------------- COLOR SWATCH APPLIED -----------------
 
# paintedTree :: [String] -> Tree a -> Tree (String, a)
def paintedTree(swatch):
'''A tree in which every node value is tupled with
a hexadecimal color string taken from a swatch list.
The first colour is used for the root node.
The next n colours paint the root's n children.
All descendants of those children are painted with
the same color as their non-root ancestor.
'''
colors = cycle(swatch)
 
def go(tree):
return fmapTree(
lambda x: ("", x)
)(tree) if not swatch else (
Node(
(next(colors), tree.get('root'))
)(
list(map(
lambda k, child: fmapTree(
lambda v: (k, v)
)(child),
colors,
tree.get('nest')
))
)
)
return go
 
 
# ---------------- GENERIC TREE FUNCTIONS ----------------
 
# Node :: a -> [Tree a] -> Tree a
def Node(v):
'''Constructor for a Tree node which connects a
value of some kind to a list of zero or
more child trees.
'''
return lambda xs: {'root': v, 'nest': xs}
 
 
# fmapTree :: (a -> b) -> Tree a -> Tree b
def fmapTree(f):
'''A new tree holding the results of
an application of f to each root in
the existing tree.
'''
def go(x):
return Node(
f(x.get('root'))
)([go(v) for v in x.get('nest')])
return go
 
 
# foldTree :: (a -> [b] -> b) -> Tree a -> b
def foldTree(f):
'''The catamorphism on trees. A summary
value defined by a depth-first fold.
'''
def go(node):
return f(
node.get('root'),
[go(x) for x in node.get('nest')]
)
return go
 
 
# levels :: Tree a -> [[a]]
def levels(tree):
'''A list of lists, grouping the root
values of each level of the tree.
'''
return [[tree.get('root')]] + list(
reduce(
zipWithLong(add),
map(levels, tree.get('nest')),
[]
)
)
 
 
# ----------------- WIKITABLE RENDERING ------------------
 
# wikiTableFromRows :: [[(String, (String, Int))]] -> String
def wikiTableFromRows(rows):
'''A wiki table rendering of rows in which each cell
has the form (hexColorString, (text, colspan))
'''
def cw(color, width):
def go(w):
return f' colspan={w}' if 1 < w else ''
return f'style="background: {color}; "{go(width)}'
 
def cellText(cell):
color, (txt, width) = cell
return f'| {cw(color,width) if txt else ""} | {txt}'
 
def go(row):
return '\n'.join([cellText(cell) for cell in row])
 
return '{| class="wikitable" ' + (
'style="text-align: center;"\n|-\n'
) + '\n|-\n'.join([go(row) for row in rows]) + '\n|}'
 
 
# ----------------------- GENERIC ------------------------
 
# ap :: (a -> b -> c) -> (a -> b) -> a -> c
def ap(f):
'''Applicative instance for functions.
'''
def go(g):
return lambda x: f(x)(g(x))
return go
 
# compose :: ((a -> a), ...) -> (a -> a)
 
 
def compose(*fs):
'''Composition, from right to left,
of a series of functions.
'''
def go(f, g):
def fg(x):
return f(g(x))
return fg
return reduce(go, fs, lambda x: x)
 
 
# head :: [a] -> a
def head(xs):
'''The first element of a non-empty list.
'''
return xs[0] if isinstance(xs, list) else next(xs)
 
 
# isSpace :: Char -> Bool
# isSpace :: String -> Bool
def isSpace(s):
'''True if s is not empty, and
contains only white space.
'''
return s.isspace()
 
 
# span :: (a -> Bool) -> [a] -> ([a], [a])
def span(p):
'''The longest (possibly empty) prefix of xs that
contains only elements satisfying p, tupled with the
remainder of xs. span p xs is equivalent to
(takeWhile p xs, dropWhile p xs).
'''
def match(ab):
b = ab[1]
return not b or not p(b[0])
 
def f(ab):
a, b = ab
return a + [b[0]], b[1:]
 
def go(xs):
return until(match)(f)(([], xs))
return go
 
 
# until :: (a -> Bool) -> (a -> a) -> a -> a
def until(p):
'''The result of repeatedly applying f until p holds.
The initial seed value is x.
'''
def go(f):
def g(x):
v = x
while not p(v):
v = f(v)
return v
return g
return go
 
 
# zipWithLong :: ((a, a) -> a) -> ([a], [a]) -> [a]
def zipWithLong(f):
'''Analogous to map(f, xs, ys)
but returns a list with the length of the *longer*
of xs and ys, taking any surplus values unmodified.
'''
def go(xs, ys):
lxs = list(xs)
lys = list(ys)
i = min(len(lxs), len(lys))
return chain.from_iterable([
map(f, lxs, lys),
lxs[i:],
lys[i:]
])
return go
 
 
# MAIN ---
if __name__ == '__main__':
main()</syntaxhighlight>
{{Out}}
{| 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)
| |
| |
|}
 
Line 1,246 ⟶ 3,259:
Strictly speaking, this is not a nested table. It is just a single level table that has some column spans > 1. For an example of using actual nested tables, see the task entry: [[Rosetta_Code/List_authors_of_task_descriptions#Raku|List_authors_of_task_descriptions#Raku]], [[Rosetta_Code/List_authors_of_task_descriptions/Full_list|(and full output)]].
 
<syntaxhighlight lang="raku" perl6line>my $outline = q:to/END/;
Display an outline as a nested table.
Parse the outline to a tree,
Line 1,305 ⟶ 3,318:
@width.push: @width[*-1] * 10;
++$depth;
@array.map: *.&insert;
--$depth;
@width.pop;
Line 1,369 ⟶ 3,382:
$depth++;
my $list = "<ol style=\"list-style: {@type[$depth - 1]};\">\n" ~
( @array.map( *.&ol ).join ) ~ "</ol>$li\n";
$depth--;
$list
Line 1,390 ⟶ 3,403:
}
( $r, $g, $b ).map( ((*+$m) * 255).Int)».base(16).join
}</langsyntaxhighlight>
 
{{out}}
Line 1,453 ⟶ 3,466:
</ol>
</div>
 
=={{header|Wren}}==
{{trans|Go}}
{{libheader|Wren-dynamic}}
{{libheader|Wren-fmt}}
<syntaxhighlight lang="wren">import "./dynamic" for Struct
import "./fmt" for Fmt
 
var NNode = Struct.create("NNode", ["name", "children"])
var INode = Struct.create("INode", ["level", "name"])
 
var toNest // recursive function
toNest = Fn.new { |iNodes, start, level, n|
if (level == 0) n.name = iNodes[0].name
var i = start + 1
while (i < iNodes.count) {
if (iNodes[i].level == level+1) {
var c = NNode.new(iNodes[i].name, [])
toNest.call(iNodes, i, level+1, c)
n.children.add(c)
} else if (iNodes[i].level <= level) {
return
}
i = i + 1
}
}
 
var makeIndent = Fn.new { |outline, tab|
var lines = outline.split("\n")
var iNodes = List.filled(lines.count, null)
var i = 0
for (line in lines) {
var line2 = line.trimStart(" ")
var le = line.count
var le2 = line2.count
var level = ((le - le2) / tab).floor
iNodes[i] = INode.new(level, line2)
i = i + 1
}
return iNodes
}
 
var toMarkup = Fn.new { |n, cols, depth|
var span = 0
var colSpan // recursive closure
colSpan = Fn.new { |nn|
var i = 0
for (c in nn.children) {
if (i > 0) span = span + 1
colSpan.call(c)
i = i + 1
}
}
 
for (c in n.children) {
span = 1
colSpan.call(c)
}
var lines = []
lines.add("{| class=\"wikitable\" style=\"text-align: center;\"")
var l1 = "|-"
var l2 = "| |"
lines.add(l1)
span = 1
colSpan.call(n)
var s = Fmt.swrite("| style=\"background: $s \" colSpan=$d | $s", cols[0], span, n.name)
lines.add(s)
lines.add(l1)
 
var nestedFor // recursive function
nestedFor = Fn.new { |nn, level, maxLevel, col|
if (level == 1 && maxLevel > level) {
var i = 0
for (c in nn.children) {
nestedFor.call(c, 2, maxLevel, i)
i = i + 1
}
} else if (level < maxLevel) {
for (c in nn.children) {
nestedFor.call(c, level+1, maxLevel, col)
}
} else {
if (nn.children.count > 0) {
var i = 0
for (c in nn.children) {
span = 1
colSpan.call(c)
var cn = col + 1
if (maxLevel == 1) cn = i + 1
var s = Fmt.swrite("| style=\"background: $s \" colspan=$d | $s", cols[cn], span, c.name)
lines.add(s)
i = i + 1
}
} else {
lines.add(l2)
}
}
}
for (maxLevel in 1...depth) {
nestedFor.call(n, 1, maxLevel, 0)
if (maxLevel < depth-1) lines.add(l1)
}
lines.add("|}")
return lines.join("\n")
}
 
var outline = """
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.
"""
var yellow = "#ffffe6;"
var orange = "#ffebd2;"
var green = "#f0fff0;"
var blue = "#e6ffff;"
var pink = "#ffeeff;"
 
var cols = [yellow, orange, green, blue, pink]
var iNodes = makeIndent.call(outline, 4)
var n = NNode.new("", [])
toNest.call(iNodes, 0, 0, n)
System.print(toMarkup.call(n, cols, 4))
 
System.print("\n")
var outline2 = """
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)
Propagating the sums upward as necessary.
and write out a table with 'colspan' values
either as a wiki table,
or as HTML.
Optionally add color to the nodes.
"""
var cols2 = [blue, yellow, orange, green, pink]
var n2 = NNode.new("", [])
var iNodes2 = makeIndent.call(outline2, 4)
toNest.call(iNodes2, 0, 0, n2)
System.print(toMarkup.call(n2, cols2, 4))</syntaxhighlight>
 
{{out}}
{| 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; " colspan=1 | measuring the indent of each line,
| style="background: #ffebd2; " colspan=1 | translating the indentation to a nested structure,
| style="background: #ffebd2; " colspan=1 | and padding the tree to even depth.
| style="background: #f0fff0; " colspan=1 | defining the width of a leaf as 1,
| style="background: #f0fff0; " colspan=1 | and the width of a parent node as a sum.
| style="background: #e6ffff; " colspan=1 | either as a wiki table,
| style="background: #e6ffff; " colspan=1 | or as HTML.
|-
| |
| |
| |
| |
| style="background: #f0fff0; " colspan=1 | (The sum of the widths of its children)
| |
| |
|}
 
 
{| class="wikitable" style="text-align: center;"
|-
| style="background: #e6ffff; " colSpan=9 | Display an outline as a nested table.
|-
| style="background: #ffffe6; " colspan=3 | Parse the outline to a tree,
| style="background: #ffebd2; " colspan=3 | count the leaves descending from each node,
| style="background: #f0fff0; " colspan=2 | and write out a table with 'colspan' values
| style="background: #ffeeff; " colspan=1 | Optionally add color to the nodes.
|-
| style="background: #ffffe6; " colspan=1 | measuring the indent of each line,
| style="background: #ffffe6; " colspan=1 | translating the indentation to a nested structure,
| style="background: #ffffe6; " colspan=1 | and padding the tree to even depth.
| style="background: #ffebd2; " colspan=1 | defining the width of a leaf as 1,
| style="background: #ffebd2; " colspan=2 | and the width of a parent node as a sum.
| style="background: #f0fff0; " colspan=1 | either as a wiki table,
| style="background: #f0fff0; " colspan=1 | or as HTML.
| |
|-
| |
| |
| |
| |
| style="background: #ffebd2; " colspan=1 | (The sum of the widths of its children)
| style="background: #ffebd2; " colspan=1 | Propagating the sums upward as necessary.
| |
| |
|}
 
=={{header|zkl}}==
<langsyntaxhighlight lang="zkl">fcn parseOutline(outline){ //--> "tree" annotated with spans
var [const] indent=" "*100; // no tabs
 
Line 1,518 ⟶ 3,740:
out.writeln("|}");
out.text
}</langsyntaxhighlight>
<langsyntaxhighlight lang="zkl">outlineText:=Data(Void,
#<<<
"Display an outline as a nested table.
Line 1,537 ⟶ 3,759:
 
rows,cols,title,trees := parseOutline(outlineText);
makeMarkup(rows,cols,title,trees).println();</langsyntaxhighlight>
{{out}}
{| class="wikitable" style="text-align: center;"
Line 1,563 ⟶ 3,785:
 
And the Raku example:
<langsyntaxhighlight lang="zkl">outlineText:=Data(Void,
#<<<
"Display an outline as a nested table.
Line 1,583 ⟶ 3,805:
 
rows,cols,title,trees := parseOutline(outlineText);
makeMarkup(rows,cols,title,trees).println();</langsyntaxhighlight>
{{out}}
{| class="wikitable" style="text-align: center;"
908

edits