Data commit
This commit is contained in:
parent
7387c8f97b
commit
cb5bb5e222
199093 changed files with 3378972 additions and 0 deletions
2
Task/Display-an-outline-as-a-nested-table/00-META.yaml
Normal file
2
Task/Display-an-outline-as-a-nested-table/00-META.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
---
|
||||
from: http://rosettacode.org/wiki/Display_an_outline_as_a_nested_table
|
||||
80
Task/Display-an-outline-as-a-nested-table/00-TASK.txt
Normal file
80
Task/Display-an-outline-as-a-nested-table/00-TASK.txt
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
{| 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)
|
||||
| |
|
||||
| |
|
||||
|}
|
||||
|
||||
The graphic representation of outlines is a staple of mind-mapping and the planning of papers, reports, and speeches.
|
||||
|
||||
;Task:
|
||||
Given a outline with at least 3 levels of indentation, for example:
|
||||
|
||||
<pre>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.</pre>
|
||||
|
||||
write a program in your language which translates your outline into a nested table, with WikiTable or HTML [[wp:Wikipedia:Advanced_table_formatting|colspan]] values attached (where needed) to parent nodes in the nested table.
|
||||
|
||||
The WikiTable at the top of this page was generated from the indented outline shown above, producing the following markup string:
|
||||
|
||||
<pre>{| 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)
|
||||
| |
|
||||
| |
|
||||
|}</pre>
|
||||
|
||||
;Extra credit:
|
||||
Use background color to distinguish the main stages of your outline, so that the subtree of each node at level two is consistently colored, and the edges between adjacent subtrees are immediately revealed.
|
||||
<br>
|
||||
|
||||
;Output:
|
||||
Display your nested table on this page.
|
||||
<br>
|
||||
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
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]
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type nNode struct {
|
||||
name string
|
||||
children []nNode
|
||||
}
|
||||
|
||||
type iNode struct {
|
||||
level int
|
||||
name string
|
||||
}
|
||||
|
||||
func toNest(iNodes []iNode, start, level int, n *nNode) {
|
||||
if level == 0 {
|
||||
n.name = iNodes[0].name
|
||||
}
|
||||
for i := start + 1; i < len(iNodes); i++ {
|
||||
if iNodes[i].level == level+1 {
|
||||
c := nNode{iNodes[i].name, nil}
|
||||
toNest(iNodes, i, level+1, &c)
|
||||
n.children = append(n.children, c)
|
||||
} else if iNodes[i].level <= level {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeIndent(outline string, tab int) []iNode {
|
||||
lines := strings.Split(outline, "\n")
|
||||
iNodes := make([]iNode, len(lines))
|
||||
for i, line := range lines {
|
||||
line2 := strings.TrimLeft(line, " ")
|
||||
le, le2 := len(line), len(line2)
|
||||
level := (le - le2) / tab
|
||||
iNodes[i] = iNode{level, line2}
|
||||
}
|
||||
return iNodes
|
||||
}
|
||||
|
||||
func toMarkup(n nNode, cols []string, depth int) string {
|
||||
var span int
|
||||
|
||||
var colSpan func(nn nNode)
|
||||
colSpan = func(nn nNode) {
|
||||
for i, c := range nn.children {
|
||||
if i > 0 {
|
||||
span++
|
||||
}
|
||||
colSpan(c)
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range n.children {
|
||||
span = 1
|
||||
colSpan(c)
|
||||
}
|
||||
var lines []string
|
||||
lines = append(lines, `{| class="wikitable" style="text-align: center;"`)
|
||||
const l1, l2 = "|-", "| |"
|
||||
lines = append(lines, l1)
|
||||
span = 1
|
||||
colSpan(n)
|
||||
s := fmt.Sprintf(`| style="background: %s " colSpan=%d | %s`, cols[0], span, n.name)
|
||||
lines = append(lines, s, l1)
|
||||
|
||||
var nestedFor func(nn nNode, level, maxLevel, col int)
|
||||
nestedFor = func(nn nNode, level, maxLevel, col int) {
|
||||
if level == 1 && maxLevel > level {
|
||||
for i, c := range nn.children {
|
||||
nestedFor(c, 2, maxLevel, i)
|
||||
}
|
||||
} else if level < maxLevel {
|
||||
for _, c := range nn.children {
|
||||
nestedFor(c, level+1, maxLevel, col)
|
||||
}
|
||||
} else {
|
||||
if len(nn.children) > 0 {
|
||||
for i, c := range nn.children {
|
||||
span = 1
|
||||
colSpan(c)
|
||||
cn := col + 1
|
||||
if maxLevel == 1 {
|
||||
cn = i + 1
|
||||
}
|
||||
s := fmt.Sprintf(`| style="background: %s " colspan=%d | %s`, cols[cn], span, c.name)
|
||||
lines = append(lines, s)
|
||||
}
|
||||
} else {
|
||||
lines = append(lines, l2)
|
||||
}
|
||||
}
|
||||
}
|
||||
for maxLevel := 1; maxLevel < depth; maxLevel++ {
|
||||
nestedFor(n, 1, maxLevel, 0)
|
||||
if maxLevel < depth-1 {
|
||||
lines = append(lines, l1)
|
||||
}
|
||||
}
|
||||
lines = append(lines, "|}")
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
func main() {
|
||||
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.`
|
||||
const (
|
||||
yellow = "#ffffe6;"
|
||||
orange = "#ffebd2;"
|
||||
green = "#f0fff0;"
|
||||
blue = "#e6ffff;"
|
||||
pink = "#ffeeff;"
|
||||
)
|
||||
cols := []string{yellow, orange, green, blue, pink}
|
||||
iNodes := makeIndent(outline, 4)
|
||||
var n nNode
|
||||
toNest(iNodes, 0, 0, &n)
|
||||
fmt.Println(toMarkup(n, cols, 4))
|
||||
|
||||
fmt.Println("\n")
|
||||
const 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.`
|
||||
cols2 := []string{blue, yellow, orange, green, pink}
|
||||
var n2 nNode
|
||||
iNodes2 := makeIndent(outline2, 4)
|
||||
toNest(iNodes2, 0, 0, &n2)
|
||||
fmt.Println(toMarkup(n2, cols2, 4))
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
{-# 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)
|
||||
<> "|}"
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
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
|
||||
}}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
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.
|
||||
}}
|
||||
|
|
@ -0,0 +1,546 @@
|
|||
(() => {
|
||||
"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 = () => {
|
||||
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.`;
|
||||
|
||||
return wikiTablesFromOutline([
|
||||
"#ffffe6",
|
||||
"#ffebd2",
|
||||
"#f0fff0",
|
||||
"#e6ffff",
|
||||
"#ffeeff"
|
||||
])(outline);
|
||||
};
|
||||
|
||||
// --------- TREE STRUCTURE FROM NESTED TEXT ---------
|
||||
|
||||
// forestFromIndentedLines :: [(Int, String)] ->
|
||||
// [Tree String]
|
||||
const forestFromIndentedLines = tuples => {
|
||||
const go = xs =>
|
||||
0 < xs.length ? (() => {
|
||||
// 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.
|
||||
return [
|
||||
Node(body)(go(tree))
|
||||
].concat(go(rest));
|
||||
})() : [];
|
||||
|
||||
return go(tuples);
|
||||
};
|
||||
|
||||
|
||||
// indentLevelsFromLines :: [String] -> [(Int, String)]
|
||||
const indentLevelsFromLines = xs => {
|
||||
const
|
||||
pairs = xs.map(
|
||||
x => bimap(length)(cs => cs.join(""))(
|
||||
span(isSpace)(list(x))
|
||||
)
|
||||
),
|
||||
indentUnit = pairs.reduce(
|
||||
(a, tpl) => {
|
||||
const i = tpl[0];
|
||||
|
||||
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 ------------
|
||||
|
||||
// paddedTree :: a -> Tree a -> Int -> Tree a
|
||||
const paddedTree = padValue =>
|
||||
// All descendants expanded to same depth
|
||||
// with empty nodes where needed.
|
||||
node => depth => {
|
||||
const go = n => tree =>
|
||||
1 < n ? (() => {
|
||||
const children = nest(tree);
|
||||
|
||||
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)}`
|
||||
);
|
||||
},
|
||||
cellText = ctw => {
|
||||
const [color, tw] = Array.from(ctw);
|
||||
const [txt, width] = Array.from(tw);
|
||||
|
||||
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 ------------------
|
||||
|
||||
// Node :: a -> [Tree a] -> Tree a
|
||||
const Node = v =>
|
||||
// Constructor for a Tree node which connects a
|
||||
// value of some kind to a list of zero or
|
||||
// more child trees.
|
||||
xs => ({
|
||||
type: "Node",
|
||||
root: v,
|
||||
nest: xs || []
|
||||
});
|
||||
|
||||
|
||||
// fmapTree :: (a -> b) -> Tree a -> Tree b
|
||||
const fmapTree = f => {
|
||||
// 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;
|
||||
};
|
||||
|
||||
|
||||
// foldTree :: (a -> [b] -> b) -> Tree a -> b
|
||||
const foldTree = f => {
|
||||
// The catamorphism on trees. A summary
|
||||
// value obtained by a depth-first fold.
|
||||
const go = tree => f(
|
||||
root(tree)
|
||||
)(
|
||||
nest(tree).map(go)
|
||||
);
|
||||
|
||||
return go;
|
||||
};
|
||||
|
||||
|
||||
// levels :: Tree a -> [[a]]
|
||||
const levels = tree => {
|
||||
// A list of lists, grouping the root
|
||||
// values of each level of the tree.
|
||||
const go = (a, node) => {
|
||||
const [h, ...t] = 0 < a.length ? (
|
||||
a
|
||||
) : [
|
||||
[],
|
||||
[]
|
||||
];
|
||||
|
||||
return [
|
||||
[node.root, ...h],
|
||||
...node.nest.slice(0)
|
||||
.reverse()
|
||||
.reduce(go, t)
|
||||
];
|
||||
};
|
||||
|
||||
return go([], tree);
|
||||
};
|
||||
|
||||
|
||||
// nest :: Tree a -> [a]
|
||||
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;
|
||||
|
||||
return "function" !== typeof xs ? (
|
||||
xs
|
||||
) : xs(root(tree));
|
||||
};
|
||||
|
||||
|
||||
// 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
|
||||
});
|
||||
|
||||
|
||||
// Nothing :: Maybe a
|
||||
const Nothing = () => ({
|
||||
type: "Maybe",
|
||||
Nothing: true
|
||||
});
|
||||
|
||||
|
||||
// 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) =>
|
||||
// A function defined by the right-to-left
|
||||
// composition of all the functions in fs.
|
||||
fs.reduce(
|
||||
(f, g) => x => f(g(x)),
|
||||
x => x
|
||||
);
|
||||
|
||||
|
||||
// cycle :: [a] -> Generator [a]
|
||||
const cycle = function* (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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// first :: (a -> b) -> ((a, c) -> (b, c))
|
||||
const first = f =>
|
||||
// A simple function lifted to one which applies
|
||||
// to a tuple, transforming only its first item.
|
||||
xy => {
|
||||
const tpl = Tuple(f(xy[0]))(xy[1]);
|
||||
|
||||
return Array.isArray(xy) ? (
|
||||
Array.from(tpl)
|
||||
) : tpl;
|
||||
};
|
||||
|
||||
|
||||
// fst :: (a, b) -> a
|
||||
const fst = tpl =>
|
||||
// First member of a pair.
|
||||
tpl[0];
|
||||
|
||||
|
||||
// isSpace :: Char -> Bool
|
||||
const isSpace = c =>
|
||||
// True if c is a white space character.
|
||||
(/\s/u).test(c);
|
||||
|
||||
|
||||
// length :: [a] -> Int
|
||||
const length = xs =>
|
||||
// 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
|
||||
"GeneratorFunction" !== xs.constructor
|
||||
.constructor.name ? (
|
||||
xs.length
|
||||
) : Infinity;
|
||||
|
||||
|
||||
// lines :: String -> [String]
|
||||
const lines = s =>
|
||||
// A list of strings derived from a single
|
||||
// string delimited by newline and or CR.
|
||||
0 < s.length ? (
|
||||
s.split(/[\r\n]+/u)
|
||||
) : [];
|
||||
|
||||
|
||||
// list :: StringOrArrayLike b => b -> [a]
|
||||
const list = xs =>
|
||||
// xs itself, if it is an Array,
|
||||
// or an Array derived from xs.
|
||||
Array.isArray(xs) ? (
|
||||
xs
|
||||
) : Array.from(xs || []);
|
||||
|
||||
|
||||
// lt (<) :: Ord a => a -> a -> Bool
|
||||
const lt = a =>
|
||||
b => a < b;
|
||||
|
||||
|
||||
// maximum :: Ord a => [a] -> a
|
||||
const maximum = xs => (
|
||||
// The largest value in a non-empty list.
|
||||
ys => 0 < ys.length ? (
|
||||
ys.slice(1).reduce(
|
||||
(a, y) => y > a ? (
|
||||
y
|
||||
) : a, ys[0]
|
||||
)
|
||||
) : undefined
|
||||
)(list(xs));
|
||||
|
||||
|
||||
// snd :: (a, b) -> b
|
||||
const snd = tpl =>
|
||||
// Second member of a pair.
|
||||
tpl[1];
|
||||
|
||||
|
||||
// span :: (a -> Bool) -> [a] -> ([a], [a])
|
||||
const span = p =>
|
||||
// Longest prefix of xs consisting of elements which
|
||||
// all satisfy p, tupled with the remainder of xs.
|
||||
xs => {
|
||||
const i = xs.findIndex(x => !p(x));
|
||||
|
||||
return -1 !== i ? (
|
||||
Tuple(xs.slice(0, i))(
|
||||
xs.slice(i)
|
||||
)
|
||||
) : Tuple(xs)([]);
|
||||
};
|
||||
|
||||
|
||||
// tail :: [a] -> [a]
|
||||
const tail = xs =>
|
||||
// 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 =>
|
||||
// The first n elements of a list,
|
||||
// string of characters, or stream.
|
||||
xs => "GeneratorFunction" !== xs
|
||||
.constructor.constructor.name ? (
|
||||
xs.slice(0, n)
|
||||
) : [].concat(...Array.from({
|
||||
length: n
|
||||
}, () => {
|
||||
const x = xs.next();
|
||||
|
||||
return x.done ? [] : [x.value];
|
||||
}));
|
||||
|
||||
|
||||
// uncons :: [a] -> Maybe (a, [a])
|
||||
const uncons = xs => {
|
||||
// Just a tuple of the head of xs and its tail,
|
||||
// Or Nothing if xs is an empty list.
|
||||
const lng = length(xs);
|
||||
|
||||
return (0 < lng) ? (
|
||||
Infinity > lng ? (
|
||||
// Finite list
|
||||
Just(Tuple(xs[0])(xs.slice(1)))
|
||||
) : (() => {
|
||||
// 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 =>
|
||||
// A list with the length of the shorter of
|
||||
// xs and ys, defined by zipping with a
|
||||
// custom function, rather than with 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();
|
||||
})();
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
using DataFrames
|
||||
|
||||
text = """
|
||||
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.
|
||||
"""
|
||||
|
||||
const bcolor = ["background: #ffffaa;", "background: #ffdddd;",
|
||||
"background: #ddffdd;", "background: #ddddff;"]
|
||||
colorstring(n) = bcolor[n == 1 ? 1 : mod1(n - 1, length(bcolor) - 1) + 1]
|
||||
|
||||
function processtable(txt)
|
||||
df = DataFrame()
|
||||
indents = Int[]
|
||||
linetext = String[]
|
||||
for line in split(txt, "\n")
|
||||
if length(line) > 0
|
||||
n = findfirst(!isspace, line)
|
||||
push!(linetext, String(line[n:end]))
|
||||
push!(indents, n - 1)
|
||||
end
|
||||
end
|
||||
len = length(indents)
|
||||
divisor = gcd(indents)
|
||||
indents .= div.(indents, divisor)
|
||||
parent(i) = (n = findlast(x -> indents[x] < indents[i], 1:i-1)) == nothing ? 0 : n
|
||||
children(i) = findall(x -> parent(x) == i, 1:len)
|
||||
treesize(i) = (s = children(i); isempty(s) ? 1 : sum(treesize, s))
|
||||
prioronlevel(i) = (j = indents[i]; filter(x -> indents[x] == j, 1:i-1))
|
||||
treesizeprior(i) = (s = prioronlevel(i); isempty(s) ? 0 : sum(treesize, s))
|
||||
startpos(i) = (n = parent(i)) == 0 ? 0 : treesizeprior(n) - treesizeprior(i)
|
||||
function leveloneparent(i)
|
||||
p = parent(i)
|
||||
return p < 1 ? 1 : p ==1 ? sum(x -> indents[x] <= 1, 1:i) : leveloneparent(p)
|
||||
end
|
||||
df.TEXT = linetext
|
||||
df.INDENT = indents
|
||||
df.COLSPAN = [treesize(i) for i in 1:len]
|
||||
df.PRESPAN = [max(0, startpos(i)) for i in 1:len]
|
||||
df.LEVELONEPARENT = [leveloneparent(i) for i in 1:len]
|
||||
return df
|
||||
end
|
||||
|
||||
function htmlfromdataframe(df)
|
||||
println("<h4>A Rosetta Code Nested Table</h4><table style=\"width:100%\" class=\"wikitable\" >")
|
||||
for ind in minimum(df.INDENT):maximum(df.INDENT)
|
||||
println("<tr>")
|
||||
for row in eachrow(df)
|
||||
if row[:INDENT] == ind
|
||||
if row[:PRESPAN] > 0
|
||||
println("<td colspan=\"$(row[:PRESPAN])\"> </td>")
|
||||
end
|
||||
print("<td ")
|
||||
if row[:COLSPAN] > 0
|
||||
println("colspan=\"$(row[:COLSPAN])\"")
|
||||
end
|
||||
println(" style = \"$(colorstring(row[:LEVELONEPARENT]))\" >$(row[:TEXT])</td>")
|
||||
end
|
||||
end
|
||||
println("</tr>")
|
||||
end
|
||||
println("</table>")
|
||||
end
|
||||
|
||||
htmlfromdataframe(processtable(text))
|
||||
textplus = text * " Optionally add color to the nodes."
|
||||
htmlfromdataframe(processtable(textplus))
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
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"]
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
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()
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my @rows;
|
||||
my $row = -1;
|
||||
my $width = 0;
|
||||
my $color = 0;
|
||||
our $bg = 'e0ffe0';
|
||||
|
||||
parseoutline( do { local $/; <DATA> =~ s/\t/ /gr } );
|
||||
|
||||
print "<table border=1 cellspacing=0>\n";
|
||||
for ( @rows )
|
||||
{
|
||||
my $start = 0;
|
||||
print " <tr>\n";
|
||||
for ( @$_ ) # columns
|
||||
{
|
||||
my ($data, $col, $span, $bg) = @$_;
|
||||
print " <td></td>\n" x ( $col - $start ),
|
||||
" <td colspan=$span align=center bgcolor=#$bg> $data </td>\n";
|
||||
$start = $col + $span;
|
||||
}
|
||||
print " <td></td>\n" x ( $width - $start ), " </tr>\n";
|
||||
}
|
||||
print "</table>\n";
|
||||
|
||||
sub parseoutline
|
||||
{
|
||||
++$row;
|
||||
while( $_[0] =~ /^( *)(.*)\n((?:\1 .*\n)*)/gm )
|
||||
{
|
||||
my ($head, $body, $col) = ($2, $3, $width);
|
||||
$row == 1 and local $bg = qw( ffffe0 ffe0e0 )[ $color ^= 1];
|
||||
if( length $body ) { parseoutline( $body ) } else { ++$width }
|
||||
push @{ $rows[$row] }, [ $head, $col, $width - $col, $bg ];
|
||||
}
|
||||
--$row;
|
||||
}
|
||||
|
||||
__DATA__
|
||||
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.
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
(phixonline)-->
|
||||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
|
||||
<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>
|
||||
<span style="color: #000000;">outlines</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"""
|
||||
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."""</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"""
|
||||
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."""</span><span style="color: #0000FF;">}</span>
|
||||
|
||||
<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>
|
||||
<span style="color: #000000;">orange</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"#ffebd2;"</span><span style="color: #0000FF;">,</span>
|
||||
<span style="color: #000000;">green</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"#f0fff0;"</span><span style="color: #0000FF;">,</span>
|
||||
<span style="color: #000000;">blue</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"#e6ffff;"</span><span style="color: #0000FF;">,</span>
|
||||
<span style="color: #000000;">pink</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"#ffeeff;"</span><span style="color: #0000FF;">,</span>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<span style="color: #004080;">integer</span> <span style="color: #000000;">span</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
|
||||
<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>
|
||||
<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 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>
|
||||
<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>
|
||||
<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;">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>
|
||||
<span style="color: #000080;font-style:italic;">-- else -- (span already 1)</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #008080;">return</span> <span style="color: #000000;">lines</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<span style="color: #000000;">children</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
|
||||
<span style="color: #004080;">string</span> <span style="color: #000000;">text</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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<span style="color: #000080;font-style:italic;">-- remove any completed parents</span>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
|
||||
<span style="color: #000000;">parent</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
|
||||
<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>
|
||||
<span style="color: #000000;">parent</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">pdx</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: #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>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #000000;">pi</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">indent</span>
|
||||
<span style="color: #000000;">pdx</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">i</span>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
|
||||
<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;">"<table class=\"wikitable\" style=\"text-align: center;\">\n"</span>
|
||||
<span style="color: #0000FF;">:</span><span style="color: #008000;">"{| class=\"wikitable\" style=\"text-align: center;\"\n"</span><span style="color: #0000FF;">)</span>
|
||||
<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>
|
||||
<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;">"<tr>\n"</span>
|
||||
<span style="color: #0000FF;">:</span><span style="color: #008000;">"|-\n"</span><span style="color: #0000FF;">)</span>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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;">"<td %s>%s</td>\n"</span>
|
||||
<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>
|
||||
<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>
|
||||
<span style="color: #000080;font-style:italic;">-- res &= iff(html?"<td></td>\n"
|
||||
-- :"| |\n")</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;">"<td %s></td>\n"</span>
|
||||
<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>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<span style="color: #008080;">if</span> <span style="color: #000000;">html</span> <span style="color: #008080;">then</span>
|
||||
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">"</tr>\n"</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</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;">"</table>\n"</span>
|
||||
<span style="color: #0000FF;">:</span><span style="color: #008000;">"|}\n"</span><span style="color: #0000FF;">)</span>
|
||||
<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>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
|
||||
<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>
|
||||
<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>
|
||||
<!--
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
"""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)
|
||||
|
|
@ -0,0 +1,364 @@
|
|||
'''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()
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
my $outline = q:to/END/;
|
||||
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.
|
||||
END
|
||||
|
||||
# Import outline paragraph into native data structure
|
||||
sub import (Str $trees, $level = ' ') {
|
||||
my $forest;
|
||||
my $last = -Inf;
|
||||
|
||||
for $trees.lines -> $branch {
|
||||
$branch ~~ / ($($level))* /;
|
||||
my $this = +$0;
|
||||
$forest ~= do {
|
||||
given $this cmp $last {
|
||||
when More { "\['{esc $branch.trim}', " }
|
||||
when Same { "'{esc $branch.trim}', " }
|
||||
when Less { "{']' x $last - $this}, '{esc $branch.trim}', " }
|
||||
}
|
||||
}
|
||||
$last = $this;
|
||||
}
|
||||
|
||||
sub esc { $^s.subst( /(<['\\]>)/, -> $/ { "\\$0" }, :g) }
|
||||
|
||||
$forest ~= ']' x 1 + $last;
|
||||
use MONKEY-SEE-NO-EVAL;
|
||||
$forest.EVAL;
|
||||
}
|
||||
|
||||
my @AoA = import $outline, ' ';
|
||||
my @layout;
|
||||
|
||||
# Collect information about node depth, position and children
|
||||
{
|
||||
my @width = 0;
|
||||
my $depth = -1;
|
||||
@AoA.&insert;
|
||||
|
||||
multi insert ($item) {
|
||||
@width[*-1]++;
|
||||
@layout.push: { :depth($depth.clone), :id(@width[*-1].clone), :text($item) };
|
||||
}
|
||||
|
||||
multi insert (@array) {
|
||||
@width.push: @width[*-1] * 10;
|
||||
++$depth;
|
||||
@array.map: &insert;
|
||||
--$depth;
|
||||
@width.pop;
|
||||
}
|
||||
}
|
||||
|
||||
my $max-depth = @layout.max( *.<depth> )<depth>;
|
||||
|
||||
# Pad ragged nodes
|
||||
for (^$max-depth) -> $d {
|
||||
my @nodes = @layout.grep( *.<depth> == $d );
|
||||
for @nodes.sort( +*.<id> ) -> $n {
|
||||
unless @layout.first( *.<id> == $n<id> ~ 1 ) {
|
||||
@layout.push: { :depth($n<depth> + 1), :id($n<id> *10 + 1), :text('') };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Calculate spans (child nodes)
|
||||
for (0..$max-depth).reverse -> $d {
|
||||
my @nodes = @layout.grep( *.<depth> == $d );
|
||||
for @nodes.sort( +*.<id> ) -> $n {
|
||||
my @span = @layout.grep: {.<depth> == $d + 1 && .<id>.starts-with: $n<id> };
|
||||
$n<span> = ( sum @span.map( { .<span> // 0} )) || +@span || 1;
|
||||
}
|
||||
}
|
||||
|
||||
# Programatically assign colors
|
||||
for (0..$max-depth) -> $d {
|
||||
my @nodes = @layout.grep( *.<depth> == $d );
|
||||
my $incr = 1 / (1 + @nodes);
|
||||
for @nodes.sort( +*.<id> ) -> $n {
|
||||
my $color = $d > 1 ??
|
||||
@layout.first( *.<id> eq $n<id>.chop )<color> !!
|
||||
"style=\"background: #" ~ hsv2rgb( ++$ * $incr, .1, 1) ~ '" ';
|
||||
$n<color> = $n<text> ?? $color !! '';
|
||||
}
|
||||
}
|
||||
|
||||
# Generate wikitable
|
||||
say '{| class="wikitable" style="text-align: center;"' ~ "\n" ~
|
||||
(join "\n|-\n", (0..$max-depth).map: -> $d {
|
||||
my @nodes = @layout.grep( *.<depth> == $d );
|
||||
(join "\n", @nodes.sort( +*.<id> ).map( -> $node {
|
||||
'| ' ~
|
||||
($node<color> // '' ) ~
|
||||
($node<span> > 1 ?? "colspan=$node<span>" !! '' ) ~
|
||||
' | ' ~ $node<text> }
|
||||
))
|
||||
}) ~ "\n|}";
|
||||
|
||||
say "\n\nSometimes it makes more sense to display an outline as...
|
||||
well... as an outline, rather than as a table." ~ Q|¯\_(ツ)_/¯| ~ "\n";
|
||||
|
||||
{ ## Outline - Ordered List #######
|
||||
my @type = <upper-roman upper-latin decimal lower-latin lower-roman>;
|
||||
my $depth = 0;
|
||||
|
||||
multi ol ($item) { "\<li>$item\n" }
|
||||
|
||||
multi ol (@array) {
|
||||
my $li = $depth ?? "</li>" !! '';
|
||||
$depth++;
|
||||
my $list = "<ol style=\"list-style: {@type[$depth - 1]};\">\n" ~
|
||||
( @array.map( &ol ).join ) ~ "</ol>$li\n";
|
||||
$depth--;
|
||||
$list
|
||||
}
|
||||
|
||||
say "<div style=\"background: #fee;\">\n" ~ @AoA.&ol ~ "</div>";
|
||||
}
|
||||
|
||||
sub hsv2rgb ( $h, $s, $v ){
|
||||
my $c = $v * $s;
|
||||
my $x = $c * (1 - abs( (($h*6) % 2) - 1 ) );
|
||||
my $m = $v - $c;
|
||||
my ($r, $g, $b) = do given $h {
|
||||
when 0..^(1/6) { $c, $x, 0 }
|
||||
when 1/6..^(1/3) { $x, $c, 0 }
|
||||
when 1/3..^(1/2) { 0, $c, $x }
|
||||
when 1/2..^(2/3) { 0, $x, $c }
|
||||
when 2/3..^(5/6) { $x, 0, $c }
|
||||
when 5/6..1 { $c, 0, $x }
|
||||
}
|
||||
( $r, $g, $b ).map( ((*+$m) * 255).Int)».base(16).join
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
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))
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
fcn parseOutline(outline){ //--> "tree" annotated with spans
|
||||
var [const] indent=" "*100; // no tabs
|
||||
|
||||
parse:=fcn(ow,tree,parent,col,prevD,unit){
|
||||
rows,span,spans,cell := 0, 0,List(), Void;
|
||||
foreach line in (ow){
|
||||
if(not line) continue;
|
||||
d,text,d := line.prefix(indent), line[d,*], d/unit; // d==0 is boo-boo
|
||||
if(d==prevD){ // assume a leaf
|
||||
rows=rows.max(d); // zero based
|
||||
col+=1; span+=1;
|
||||
cell=List(d,col,1,text); // cell: (depth, col offset, span, text)
|
||||
tree.append(cell);
|
||||
}
|
||||
else if(d>prevD){ // down a level
|
||||
ow.push(line);
|
||||
r,s := self.fcn(ow,tree,cell,col-1,d,unit);
|
||||
rows = rows.max(r);
|
||||
spans.append(s);
|
||||
}
|
||||
else{ // d<prevD: done with this branch, back out to level above
|
||||
ow.push(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
span=( spans and (spans.sum(0) + span - 1) or span ).max(1);
|
||||
parent[2]=span;
|
||||
return(rows,span);
|
||||
};
|
||||
|
||||
ow,title,trees := outline.walker(11), ow.next(), List();
|
||||
line,unit := ow.peek(), line.prefix(indent); // no leading space == bad
|
||||
rows,cols := 0,0;
|
||||
foreach line in (ow){ // the major (same color) columns
|
||||
tree:=List(0, cell:=List(1, 1,1, line.strip()) );
|
||||
trees.append(tree);
|
||||
r,c := parse(ow,tree,cell,0,2,unit);
|
||||
tree[0]=c; // span for this "branch"
|
||||
rows,cols = rows.max(r), cols + c;
|
||||
}
|
||||
return(rows+1,cols,title,trees);
|
||||
}
|
||||
|
||||
fcn makeMarkup(rows,cols,title,trees){
|
||||
var [const] colors=L("#ffebd2","#f0fff0","#e6ffff","#ffeeff");
|
||||
out,cell := Data(Void), 0'~| style="background: %s " colspan=%d | %s~.fmt;
|
||||
out.writeln(0'~{| class="wikitable" style="text-align: center;"~,"\n|-\n",
|
||||
cell("#ffffe6;",cols,title));
|
||||
foreach row in ([1..rows-1]){
|
||||
clrs:=Walker.cycle(colors);
|
||||
out.writeln("|-");
|
||||
foreach t in (trees){ // create this row
|
||||
span,clr := t[0], clrs.next();
|
||||
col,cols := 1, t[1,*].filter('wrap([(d,_,text)]){ d==row });
|
||||
foreach _,cpos,cspan,text in (cols){
|
||||
if(col<cpos){ out.writeln(cell(clr,cpos-col,"")); col=cpos }
|
||||
out.writeln(cell(clr,cspan,text)); col+=cspan;
|
||||
} // col is span+1 after loop if all cells had text
|
||||
if(col<=span) out.writeln(cell(clr,span-col+1,""));
|
||||
}
|
||||
}
|
||||
out.writeln("|}");
|
||||
out.text
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
outlineText:=Data(Void,
|
||||
#<<<
|
||||
"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.
|
||||
");
|
||||
#<<<
|
||||
|
||||
rows,cols,title,trees := parseOutline(outlineText);
|
||||
makeMarkup(rows,cols,title,trees).println();
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
outlineText:=Data(Void,
|
||||
#<<<
|
||||
"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.
|
||||
");
|
||||
#<<<
|
||||
|
||||
rows,cols,title,trees := parseOutline(outlineText);
|
||||
makeMarkup(rows,cols,title,trees).println();
|
||||
Loading…
Add table
Add a link
Reference in a new issue