# rendering a tree with a single child
#
# To run (on Linux and x86):
# $ git clone https://github.com/akkartik/mu
# $ cd mu
# $ ./translate_mu prototypes/tile/4.mu
# $ ./a.elf
#
# You should see a single rectangle representing a singleton tree node.
# Press a key. You should see the tree grow a single child.
# It seems useful as a visual idiom to represent nodes with a single child as
# slightly larger than the child.
# Once we get to multiple children we'll start tiling more regularly.
type cell {
val: int # single chars only for now
parent: (handle cell)
first-child: (handle cell)
next-sibling: (handle cell)
prev-sibling: (handle cell)
}
fn main -> exit-status/ebx: int {
var root-handle: (handle cell)
var root/esi: (addr handle cell) <- address root-handle
allocate root
var cursor/edi: (addr handle cell) <- copy root
enable-keyboard-immediate-mode
var root-addr/eax: (addr cell) <- lookup *root
render root-addr
$main:loop: {
# process key
{
var c/eax: byte <- read-key
compare c, 4 # ctrl-d
break-if-= $main:loop
process c, root, cursor
}
# render tree
root-addr <- lookup root-handle
render root-addr
loop
}
clear-screen
enable-keyboard-type-mode
exit-status <- copy 0
}
#######################################################
# Tree mutations
#######################################################
fn process c: byte, root: (addr handle cell), cursor: (addr handle cell) {
var c1/eax: (addr handle cell) <- copy cursor
var c2/eax: (addr cell) <- lookup *c1
create-child c2
}
fn create-child node: (addr cell) {
var n/ecx: (addr cell) <- copy node
var first-child/esi: (addr handle cell) <- get n, first-child
allocate first-child
}
#######################################################
# Tree drawing
#######################################################
fn render root: (addr cell) {
clear-screen
var depth/eax: int <- tree-depth root
var viewport-width/ecx: int <- copy 0x64 # col2
viewport-width <- subtract 5 # col1
var column-width/eax: int <- try-divide viewport-width, depth
render-tree root, column-width, 5, 5, 0x20, 0x64
}
fn render-tree c: (addr cell), column-width: int, row-min: int, col-min: int, row-max: int, col-max: int {
var root-max/ecx: int <- copy col-min
root-max <- add column-width
draw-box row-min, col-min, row-max, root-max
var c2/eax: (addr cell) <- copy c
var child/eax: (addr handle cell) <- get c2, first-child
var child-addr/eax: (addr cell) <- lookup *child
{
compare child-addr, 0
break-if-=
increment row-min
decrement row-max
render-tree child-addr, column-width, row-min, root-max, row-max, col-max
}
}
fn tree-depth node-on-stack: (addr cell) -> result/eax: int {
var tmp-result/edi: int <- copy 0
var node/eax: (addr cell) <- copy node-on-stack
var child/ecx: (addr handle cell) <- get node, first-child
var child-addr/eax: (addr cell) <- lookup *child
{
compare child-addr, 0
break-if-=
{
var tmp/eax: int <- tree-depth child-addr
compare tmp, tmp-result
break-if-<=
tmp-result <- copy tmp
}
child <- get child-addr, next-sibling
child-addr <- lookup *child
loop
}
result <- copy tmp-result
result <- increment
}
# slow, iterative divide instruction
fn try-divide _nr: int, _dr: int -> result/eax: int {
result <- copy _nr
result <- shift-right 2
#? var nr/ecx: int <- copy _nr
#? var tmp/ecx: int <- copy 2
#? # find nearest power of 2
#? {
#? k
#? }
}
fn draw-box row1: int, col1: int, row2: int, col2: int {
draw-horizontal-line row1, col1, col2
draw-vertical-line row1, row2, col1
draw-horizontal-line row2, col1, col2
draw-vertical-line row1, row2, col2
}
fn draw-horizontal-line row: int, col1: int, col2: int {
var col/eax: int <- copy col1
move-cursor-on-screen row, col
{
compare col, col2
break-if->=
print-string-to-screen "-"
col <- increment
loop
}
}
fn draw-vertical-line row1: int, row2: int, col: int {
var row/eax: int <- copy row1
{
compare row, row2
break-if->=
move-cursor-on-screen row, col
print-string-to-screen "|"
row <- increment
loop
}
}