diff options
Diffstat (limited to 'ipl/mprogs/algae.icn')
-rw-r--r-- | ipl/mprogs/algae.icn | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/ipl/mprogs/algae.icn b/ipl/mprogs/algae.icn new file mode 100644 index 0000000..1a92952 --- /dev/null +++ b/ipl/mprogs/algae.icn @@ -0,0 +1,356 @@ +######################################################################### +# +# File: algae.icn +# +# Subject: Program to show expression evaluation as ``algae'' +# +# Author: Clinton Jeffery +# +# Date: November 22, 1997 +# +######################################################################### +# +# This file is in the public domain. +# +############################################################################ +# +# Press ESC or q to quit +# Left mouse assigns specific (row,column) break "points" +# Middle mouse assigns absolute depth and width break lines +# Right button erases assigned break "points" +# +# When paused due to a break, you can: +# +# c to continue +# s to single step +# C to clear one point and continue +# " " to clear everything and continue +# +######################################################################### +# +# Requires: Version 9 graphics +# +############################################################################ +# +# Includes: evdefs.icn +# +############################################################################ + +$include "evdefs.icn" + +link evinit +link evutils +link options +link optwindw +link hexlib +link evaltree + +global scale, # cell (hexagon or square) size + step, # single step mode + numrows, # number of cell rows + numcols, # number of cell columns + spot, # cell-fill procedure (hex or square) + mouse, # cell-mouse-locator procedure + Visualization, # the window + wHexOutline, # binding for drawing cell outlines + depthbound, # call-depth on which to break + breadthbound, # suspension-width on which to break + hotspots # table of individual cells on which to break + +record algae_activation(node, row, column, parent, children, color) + + +# +# main() - program entry point. The main loop is in evaltree(). +# +procedure main(av) + local codes, algaeoptions + # + # pull off algae options (don't consume child's options in this call + # to options()). + # + algaeoptions := [] + while av[1][1] == "-" do { + put(algaeoptions, pop(av)) + if algaeoptions[-1] == "-f" then put(algaeoptions, pop(av)) + } + EvInit(av) | stop("Can't EvInit ",av[1]) + codes := algae_init(algaeoptions) + evaltree(codes, algae_callback, algae_activation) + WAttrib("windowlabel=Algae: finished") + EvTerm(&window) +end + +# +# algae_init() - initialization and command-line processing. +# This procedure supplies default behavior and handles options. +# +procedure algae_init(algaeoptions) + local t, position, geo, codes, i, cb, coord, e, s, x, y, m, row, column + t := options(algaeoptions, + winoptions() || "P:S:-geo:-square!-func!-scan!-op!-noproc!") + /t["L"] := "Algae" + /t["B"] := "cyan" + scale := \t["S"] | 12 + if \t["square"] then { + spot := square_spot + mouse := square_mouse + } + else { + scale /:= 4 + spot := hex_spot + mouse := hex_mouse + } + codes := cset(E_MXevent) + if /t["noproc"] then codes ++:= ProcMask + if \t["scan"] then codes ++:= ScanMask + if \t["func"] then codes ++:= FncMask + if \t["op"] then codes ++:= OperMask + hotspots := table() + &window := Visualization := optwindow(t) | stop("no window") + numrows := (XHeight() / (scale * 4)) + numcols := (XWidth() / (scale * 4)) + wHexOutline := Color("white") # used by the hexagon library + if /t["square"] then starthex(Color("black")) + return codes +end + +# +# algae_callback() - evaltree callback procedure for algae. +# Called for each event, it updates the screen to correspond +# to the change in the activation tree. +# +procedure algae_callback(new, old) + local coord, e + initial { + old.row := old.parent.row := 0; old.column := old.parent.column := 1 + } + case &eventcode of { + !CallCodes: { + new.column := (old.children[-2].column + 1 | computeCol(old)) | stop("eh?") + new.row := old.row + 1 + new.color := Color(&eventcode) + spot(\old.color, old.row, old.column) + } + !ReturnCodes | + !FailCodes: spot(Color("light blue"), old.row, old.column) + !SuspendCodes | + !ResumeCodes: spot(old.color, old.row, old.column) + !RemoveCodes: { + spot(Color("black"), old.row, old.column) + WFlush(Color("black")) + delay(100) + spot(Color("light blue"), old.row, old.column) + } + E_MXevent: do1event(&eventvalue, new) + } + spot(Color("yellow"), new.row, new.column) + coord := location(new.column, new.row) + if \step | (\breadthbound <= new.column) | (\depthbound <= new.row) | + \ hotspots[coord] then { + step := &null + WAttrib("windowlabel=Algae stopped: (s)tep (c)ont ( )clear ") + while e := Event() do + if do1event(e, new) then break + WAttrib("windowlabel=Algae") + if \ hotspots[coord] then spot(Color("light blue"), new.row, new.column) + } +end + + +# +# procedures for the "-square" option, display Algae using squares +# instead of hexagons. +# + +# Draw a square at (row, column) +procedure square_spot(w, row, column) + FillRectangle(w, (column - 1) * scale, (row - 1) * scale, scale, scale) +end + + +# encode a location value (base 1) for a given x and y pixel +procedure square_mouse(y, x) + return location(x / scale + 1, y / scale + 1) +end + +# +# clearspot() removes a "breakpoint" at (x,y) +# +procedure clearspot(spot) + local x, y, s2, x2, y2 + + hotspots[spot] := &null + y := vertical(spot) + x := horizontal(spot) + every s2 := \!hotspots do { + x2 := horizontal(s2) + y2 := vertical(s2) + } + spot(Visualization, y, x) +end + +# +# setspot() sets a breakpoint at (x,y) and marks it orange +# +procedure setspot(loc) + local x, y + + hotspots[loc] := loc + y := vertical(loc) + x := horizontal(loc) + spot(Color("orange"), y, x) +end + +# +# do1event() processes a single user input event. +# +procedure do1event(e, new) + local m, xbound, ybound, row, column, x, y, s, p + + case e of { + "q" | + "\e": exit() + "s": { # execute a single step + step := 1 + return + } + "C": { # clear a single break point + clearspot(location(new.column, new.row)) + return + } + " ": { # space character: clear all break points + if \depthbound then { + every y := 1 to numcols do { + if not who_is_at(depthbound, y, new) then + spot(Visualization, depthbound, y) + } + } + if \breadthbound then { + every x := 1 to numrows do { + if not who_is_at(x, breadthbound, new) then + spot(Visualization, x, breadthbound) + } + } + every s := \!hotspots do { + x := horizontal(s) + y := vertical(s) + spot(Visualization, y, x) + } + hotspots := table() + depthbound := breadthbound := &null + return + } + &mpress | &mdrag: { # middle button: set bound box break lines + if m := mouse(&y, &x) then { + row := vertical(m) + column := horizontal(m) + if \depthbound then { # erase previous bounding box, if any + every spot(Visualization, depthbound, 1 to breadthbound) + every spot(Visualization, 1 to depthbound, breadthbound) + } + depthbound := row + breadthbound := column + # + # draw new bounding box + # + every x := 1 to breadthbound do { + if not who_is_at(depthbound, x, new) then + spot(Color("orange"), depthbound, x) + } + every y := 1 to depthbound - 1 do { + if not who_is_at(y, breadthbound, new) then + spot(Color("orange"), y, breadthbound) + } + } + } + &lpress | &ldrag: { # left button: toggle single cell breakpoint + if m := mouse(&y, &x) then { + xbound := horizontal(m) + ybound := vertical(m) + if hotspots[m] === m then + clearspot(m) + else + setspot(m) + } + } + &rpress | &rdrag: { # right button: report node at mouse location + if m := mouse(&y, &x) then { + column := horizontal(m) + row := vertical(m) + if p := who_is_at(row, column, new) then + WAttrib("windowlabel=Algae " || image(p.node)) + } + } + } +end + +# +# who_is_at() - find the activation tree node at a given (row, column) location +# +procedure who_is_at(row, col, node) + while node.row > 1 & \node.parent do + node := node.parent + return sub_who(row, col, node) # search children +end + +# +# sub_who() - recursive search for the tree node at (row, column) +# +procedure sub_who(row, column, p) + local k + if p.column === column & p.row === row then return p + else { + every k := !p.children do + if q := sub_who(row, column, k) then return q + } +end + +# +# computeCol() - determine the correct column for a new child of a node. +# +procedure computeCol(parent) + local col, x, node + node := parent + while \node.row > 1 do # find root + node := \node.parent + if node === parent then return parent.column + if col := subcompute(node, parent.row + 1) then { + return max(col, parent.column) + } + else return parent.column +end + +# +# subcompute() - recursive search for the leftmost tree node at depth row +# +procedure subcompute(node, row) + # check this level for correct depth + if \node.row = row then return node.column + 1 + # search children from right to left + return subcompute(node.children[*node.children to 1 by -1], row) +end + +# +# Color(s) - return a binding of &window with foreground color s; +# allocate at most one binding per color. +# +procedure Color(s) + static t, magenta + initial { + magenta := Clone(&window, "fg=magenta") | stop("no magenta") + t := table() + /t[E_Fcall] := Clone(&window, "fg=red") | stop("no red") + /t[E_Ocall] := Clone(&window, "fg=chocolate") | stop("no chocolate") + /t[E_Snew] := Clone(&window, "fg=purple") | stop("no purple") + } + if *s > 1 then + / t[s] := Clone(&window, "fg=" || s) | stop("no ",image(s)) + else + / t[s] := magenta + return t[s] +end + +procedure max(x,y) + if x < y then return y else return x +end |