Initial prototype for an outline as a database

Ideally the Outline as a hierarchy of database queries functionality would be integrated with software such as tkoutline. For an initial prototype, I wrote a script that can be run as a separate process that will convert a tkoutline data file containing rules and properties into another tkoutline file with all the rules and queries applied.

Eventually, I hope to write code that will run from within tkoutline and automatically refresh the outline based on changes to rules and facts.

In order to use the following script the SWI prolog interpreter [1] needs to be installed and the script should be modified to point to the location of your interpreter. Another requirement is that you make sense of my gibberish instructions. Good luck.

To test it out, create an outline with tkoutline that looks like the following:

 [-] Node with derived children
	 .  # Nodes beginning with "#" are ignored
	 .  # If a node starts with ":", then all the following text should be valid prolog facts or clauses
	[-] # The prolog fact or clause is first run through Tcl variable and function substitution
		 .  # The tcl variables "tree" and "node" are made available for substitution
		 .  # The variable "parent" is also made available as a convenient shortcut to [$tree parent $node]
		 .  # See tcllib tree manpages for more details
	 .  # The following prolog says to make any node with the project=tkoutline be a child of the above parent
	 .  :child($parent,Y):-project(Y,tkoutline).
 [-] Node with explicit children
	[-] child1
		[-] child1.1
			 .  :project($parent,tkoutline).
	[-] child2
		 .  :project($parent, 'Just for fun').

When run through the script later in this page, the above outline will be transformed into the following intermediate prolog facts/clauses:

 child(root,node1).
 description(node1,'Node with derived children').
 child(node1,Y):-project(Y,tkoutline).
 child(root,node11).
 description(node11,'Node with explicit children').
 child(node11,node12).
 description(node12,'child1').
 child(node12,node14).
 description(node14,'child1\.1').
 project(node14,tkoutline).
 child(node11,node13).
 description(node13,'child2').
 project(node13, 'Just for fun').

The script will continue and generate a tkoutline file for the following outline:

 [-] Node with derived children
	 .  child1.1
 [-] Node with explicit children
	[-] child1
		 .  child1.1
	 .  child2

Here is the script

 package require tcllib
 package require struct
 # Returns a script that will regenerate the given node and its  descendents
 # Swiped from texttree.tcl
 proc nodeToScript {tree node} {
    set script "\$t insert [$tree parent $node] [expr [$tree index $node] + 1] $node\n"
    append script "\$t set $node [list [$tree set $node]]\n"
    append script "\$t set $node -key expand 1\n"
    foreach child [$tree children $node] {
        append script [nodeToScript $tree $child]
    }
    return $script
 }

 # Returns a script that will regenerate the given tree
 # Swiped from texttree.tcl
 proc treeToScript {tree} {
    set script "set t \[::struct::tree]\n"
    append script "\$t set root -key expand 1\n"
    foreach child [$tree children root] {
        append script [nodeToScript $tree $child]
    }

    # Last line of the script returns the name of the tree
    append script "return \$t\n";
    return $script
 }

 # Converts the given tree to prolog facts and clauses
 #
 # Foreach node "c" a fact is asserted relating that node to its
 # parent node "p" -- child(p,c).
 #
 # The data for each node is related to its node id, "n" by the fact
 # description(n,data).
 #
 # If the first character of a node's data is "#", then the node is ignored
 #
 # If the first character of a node's data is ":", then Tcl substitution
 # is performed on the node's data and the result is assert in the
 # Prolog database.
 proc treeToProlog {tree} {
    set output {}
    $tree walk root -command {
        set tree %t
        set node %n
        if {$node != "root"} {
            set data [$tree set $node]
            set parent [$tree parent $node]
            switch -- [string index $data 0] {
                ":" {
                    catch {subst -nobackslash [string range $data 1 end]} result
                    append output "$result\n"
                    }
                "#" {}
                default {
                    append output "child($parent,$node).\n"
                    append output "description($node,'[string map {' \\' . \\.} $data]').\n"
                    }
                }
        }
    }
    return $output
 }

 # Converts the given graph into a tree
 proc graphToTree {graph graphNode tree treeNode} {
    foreach child [$graph nodes -out $graphNode] {
        set n [$tree insert $treeNode [$tree numchildren $treeNode]]
        $tree set $n [$graph node set $child]
        graphToTree $graph $child $tree $n
    }
    return
 }

 # Prolog code snippet that will output Tcl code that will create
 # a graph from a prolog database.
 set prologToTclGraph {
    nodeData(Node,Data):-
        description(Node,Data).
    nodeData(Node,Node).

    % Display Tcl graph code for a single node
    writeNode(Node):-
        writef('$g node insert {%w}\n', [Node]),
        nodeData(Node,Data),
        writef('$g node set {%w} {%w}\n',[Node,Data]).

    % Display Tcl code for a list of nodes
    writeNodes([]).
    writeNodes([H|T]):-
        writeNode(H),
        writeNodes(T).

    % Display Tcl code to create arcs connecting nodes
    writeArcs(_,[]).
    writeArcs(Parent,[H|T]):-
        writef('$g arc insert {%w} {%w}\n', [Parent,H]),
        clause(childGoal(H,X),Body),
        findall(X,Body,Children),
        writeArcs(H,Children),
        writeArcs(Parent,T).
    node(X):-child(_,X).

    % Defines the child predicate as the default parent/child relationship
    % Each node can define it's own predicate to override this
    childGoal(X,Y):-child(X,Y).

    % The main entry point
    main:-
        write_ln('set g [::struct::graph]'),
        writeNode(root),
        setof(X, node(X), Nodes),
        writeNodes(Nodes),
        clause(childGoal(root,X),Body),
        findall(X,Body,Children),
        writeArcs(root,Children),
        write_ln('set g').
 }
 #
 # Main code
 #

 # Input argument should be the filename of a tkoutline file
 set tree [source [lindex $argv 0]]

 # The location of the prolog interpreter
 set prologPath d:/apps/prolog/bin
 set prologExe $prologPath/plcon

 # Convert the tree to prolog facts and clauses and append the
 # above prolog code
 set prologScript "[treeToProlog $tree]\n$prologToTclGraph"

 # Write the prolog code to a temporary file
 set f [open "temp.pro" w]
 puts $f $prologScript
 close $f

 # Invoke the prolog interpreter and evaluate the result
 set graphScript [exec $prologExe -s temp.pro -t main 2>@ stderr]
 set graph [eval $graphScript]

 # Convert the graph to a tree and write it to stdout
 set tree [struct::tree]
 graphToTree $graph root $tree root
 puts [treeToScript $tree]

 # It is up to the user to redirect the result into a file and
 # load it into tkoutline.


Updated 8 Jan 2002, 03:23 GMT
Search - Recent Changes - Reference - About WiKit - Go to Tkoutline - Help
Sourceforge logo