treeql85.tcl

Go to the documentation of this file.
00001 /*  treeql.tcl*/
00002 /*  A generic tree query language in snit*/
00003 /* */
00004 /*  Copyright 2004 Colin McCormack.*/
00005 /*  You are permitted to use this code under the same license as tcl.*/
00006 /* */
00007 /*  20040930 Colin McCormack - initial release to tcllib*/
00008 /* */
00009 /*  RCS: @(#) $Id: treeql85.tcl,v 1.2 2007/05/01 17:00:25 andreas_kupries Exp $*/
00010 
00011 package require Tcl 8.5
00012 package require snit
00013 package require struct::list
00014 package require struct::
00015 
00016 snit = ::type ::treeql {
00017     variable nodes  ;/*  set of all nodes*/
00018     variable tree   ;/*  tree over which nodes are defined*/
00019     variable query  ;/*  full query - ie: 'parent' of this treeql object*/
00020 
00021     /*  low level accessor to tree*/
00022     ret  treeObj () {
00023     return $tree
00024     }
00025 
00026     /*  apply the [$tree cmd {*}$args] form to each node*/
00027     /*  returns the list of results of application*/
00028     ret  apply (type cmd , type args) {
00029     set result {}
00030     foreach node $nodes {
00031         if {[catch {
00032         $tree {*}$cmd $node {*}$args
00033         } application eo]} {
00034         puts stderr "apply ERROR: $tree $cmd $node $args -> $application - $eo"
00035         } else {
00036         #puts stderr "Apply: $tree $cmd $node $args -> $application"
00037         lappend result {*}$application
00038         }
00039     }
00040 
00041     return $result
00042     }
00043 
00044     /*  filter nodes by [$tree cmd {*}$args]*/
00045     /*  returns the list of results of application when application is non nil*/
00046     ret  filter (type cmd , type args) {
00047     set result {}
00048     foreach node $nodes {
00049         if {[catch {
00050         $tree {*}$cmd $node {*}$args
00051         } application eo]} {
00052         puts stderr "filter ERROR: $tree $cmd $node $args -> $application - $eo"
00053         } else {
00054         #puts stderr "Filter: $tree $cmd $node $args -> $application"
00055         if {$application != {}} {
00056             lappend result $application
00057         }
00058         }
00059     }
00060     return $result
00061     }
00062 
00063     /*  filter nodes by the predicate [$tree cmd {*}$args]*/
00064     /*  returns the list of results of application when application is true*/
00065     ret  bool (type cmd , type args) {
00066 
00067     #puts stderr "Bool: $tree $cmd - $args"
00068     #set result [::struct::list filter $nodes [list $tree $cmd {*}$args]]
00069     #puts stderr "Bool: $tree $cmd - $nodes - $args -> $result"
00070     #return $result
00071 
00072     # replaced by tcllib's list filter
00073     set result {}
00074     foreach node $nodes {
00075         if {[catch {
00076         $tree {*}$cmd $node {*}$args
00077         } application eo]} {
00078         puts stderr "bool ERROR: $tree $cmd $node $args -> $application - $eo"
00079         } else {
00080         #puts stderr "Bool: $tree $cmd $node $args -> $application - [$tree dump $node]"
00081         if {$application} {
00082             lappend result $node
00083         }
00084         }
00085     }
00086 
00087     return $result
00088     }
00089 
00090     /*  applyself - map cmd on $self to each node, discarding null results*/
00091     ret  applyself (type cmd , type args) {
00092 
00093     set result {}
00094     foreach node $nodes {
00095         if {[catch {
00096         $query {*}$cmd $node {*}$args
00097         } application eo]} {
00098         puts stderr "applyself ERROR: $tree $cmd $node $args -> $application - $eo"
00099         } else {
00100         if {[llength $application]} {
00101             lappend result {*}$application
00102         }
00103         }
00104     }
00105 
00106     return $result
00107     }
00108 
00109     /*  mapself - map cmd on $self to each node*/
00110     ret  mapself (type cmd , type args) {
00111 
00112     set result {}
00113     foreach node $nodes {
00114         if {[catch {
00115         $query {*}$cmd $node {*}$args
00116         } application eo]} {
00117         puts stderr "mapself ERROR: $tree $cmd $node $args -> $application - $eo"
00118         } else {
00119         #puts stderr "Mapself: $query $cmd $node $args -> $application"
00120         lappend result $application
00121         }
00122     }
00123 
00124     return $result
00125     }
00126 
00127     /*  shim to perform operation $op on attribute $attr of $node*/
00128     ret  do_attr (type node , type op , type attr) {
00129     set attrv [$tree get $node $attr]
00130     #puts stderr "$self do_attr node:'$node' op:'$op' attr:'$attr' attrv:'$attrv'"
00131     return [{*}$op $attrv]
00132     }
00133 
00134     /*  filter nodes by predicate [string $op] over attribute $attr*/
00135     ret  stringP (type op , type attr , type args) {
00136     set n {}
00137     set map [$self mapself do_attr [list string {*}$op] $attr]
00138     foreach result $map node $nodes {
00139         #puts stderr "$self stringP $op $attr -> $result - $node"
00140         if {$result} {
00141         lappend n $node
00142         }
00143     }
00144     set nodes $n
00145     return $args
00146     }
00147 
00148     /*  filter nodes by negated predicate [string $op] over attribute $attr*/
00149     ret  stringNP (type op , type attr , type args) {
00150     set n {}
00151     set map [$self mapself do_attr [list string {*}$op] $attr]
00152     foreach result $map node $nodes {
00153         if {!$result} {
00154         lappend n $node
00155         }
00156     }
00157     set nodes $n
00158     return $args
00159     }
00160 
00161     /*  filter nodes by predicate [expr {*}$op] over attribute $attr*/
00162     ret  exprP (type op , type attr , type args) {
00163     set n {}
00164     set map [$self mapself do_attr [list expr {*}$op] $attr]
00165     foreach result $map node $nodes {
00166         if {$result} {
00167         lappend n $node
00168         }
00169     }
00170     set nodes $n
00171     return $args
00172     }
00173 
00174     /*  filter nodes by predicate ![expr {*}$op] over attribute $attr*/
00175     ret  exprNP (type op , type attr , type args) {
00176     set n {}
00177     set map [$self mapself do_attr [list expr {*}$op] $attr]
00178     foreach result $map node $nodes {
00179         if {!$result} {
00180         lappend n $node
00181         }
00182     }
00183     set nodes $n
00184     return $args
00185     }
00186 
00187     /*  shim to return string values of attributes matching $pattern of a given $node*/
00188     ret  do_get (type node , type pattern) {
00189     set result {}
00190     foreach key [$tree keys $node $pattern] {
00191         set result [concat $result [$tree get $node $key]]
00192     }
00193     return $result
00194     }
00195 
00196     /*  Returns list of attribute values of attributes matching $pattern -*/
00197     ret  get (type pattern) {
00198     set nodes [$self mapself do_get $pattern]
00199     return {}   ;# terminate query
00200     }
00201 
00202     /*  Returns list of attribute values of the current node, in an unspecified order.*/
00203     ret  attlist () {
00204     $self get *
00205     return {}   ;# terminate query
00206     }
00207 
00208     /*  Returns list of lists of attributes of each node*/
00209     ret  attrs (type glob) {
00210     set nodes [$self apply keys $glob]
00211     return {}   ;# terminate query
00212     }
00213 
00214     /*  shim to find node ancestors by repetitive [parent]*/
00215     /*  as tcllib tree lacks this*/
00216     ret  do_ancestors (type node) {
00217     set ancestors {}
00218     set rootname [$tree rootname]
00219     while {$node ne $rootname} {
00220         lappend ancestors $node
00221         set node [$tree parent $node]
00222     }
00223     lappend ancestors $rootname
00224     return $ancestors
00225     }
00226 
00227     /*  path from node to root*/
00228     ret  ancestors (type args) {
00229     set nodes [$self applyself do_ancestors]
00230     return $args
00231    }
00232 
00233     /*  shim to find $node rootpath by repetitive [parent]*/
00234     /*  as tcllib tree lacks this*/
00235     ret  do_rootpath (type node) {
00236     set ancestors {}
00237     set rootname [$tree rootname]
00238     while {$node ne $rootname} {
00239         lappend ancestors $node
00240         set node [$tree parent $node]
00241     }
00242     lappend ancestors $rootname
00243     return [::struct::list reverse $ancestors]
00244     }
00245 
00246     /*  path from root to node*/
00247     ret  rootpath (type args) {
00248     set nodes [$self applyself do_rootpath]
00249     return $args
00250     }
00251 
00252     /*  node parent*/
00253     ret  parent (type args) {
00254     set nodes [$self apply parent]
00255     return $args
00256     }
00257 
00258     /*  node children*/
00259     ret  children (type args) {
00260     set nodes [$self apply children]
00261     return $args
00262     }
00263 
00264     /*  previous sibling*/
00265     ret  left (type args) {
00266     set nodes [$self apply previous]
00267     return $args
00268     }
00269 
00270     /*  next sibling*/
00271     ret  right (type args) {
00272     set nodes [$self apply next]
00273     return $args
00274     }
00275 
00276     /*  shim to find left siblings of node, in order of occurrence*/
00277     ret  do_previous* (type node) {
00278     if {$node == [$tree rootname]} {
00279         set children $node
00280     } else {
00281         set children [$tree children [$tree parent $node]]
00282     }
00283     set index [expr {[lsearch $children $node] - 1}]
00284     return [lrange $children 0 $index]
00285     }
00286 
00287     /*  previous siblings in reverse order*/
00288     ret  prev (type args) {
00289     set nodes [::struct::list reverse [$self applyself do_previous*]]
00290     return $args
00291     }
00292 
00293     /*  previous siblings in tree order*/
00294     ret  esib (type args) {
00295     set nodes [$self applyself do_previous*]
00296     return $args
00297     }
00298 
00299     /*  shim to find next siblings in tree order*/
00300     ret  do_next* (type node) {
00301     if {$node == [$tree rootname]} {
00302         set children $node
00303     } else {
00304         set children [$tree children [$tree parent $node]]
00305     }
00306     set index [expr {[lsearch $children $node] + 1}]
00307     return [lrange $children $index end]
00308     }
00309 
00310     /*  next siblings in tree order*/
00311     ret  next (type args) {
00312     set nodes [$self applyself do_next*]
00313     return $args
00314     }
00315 
00316     /*  generates the tree root*/
00317     ret  root (type args) {
00318     set nodes [$tree rootname]
00319     return $args
00320     }
00321 
00322     /*  shim to calculate descendants*/
00323     ret  do_subtree (type node) {
00324     set nodeset $node
00325     set children [$tree children $node]
00326     foreach child $children {
00327         set descendants [$self do_subtree $child]
00328         lappend nodeset {*}$descendants
00329     }
00330     #puts stderr "do_subtree $node -> $nodeset"
00331     return $nodeset
00332     }
00333 
00334     /*  generates proper-descendants of nodes*/
00335     ret  descendants (type args) {
00336     set desc {}
00337     set nodeset {}
00338     foreach node $nodes {
00339         set subtree [$self do_subtree $node]
00340         set descendants [lrange $subtree 1 end]
00341         lappend nodeset {*}$descendants
00342     }
00343     set nodes $nodeset
00344     return $args
00345     }
00346 
00347     /*  generates all subtrees rooted at node*/
00348     ret  subtree (type args) {
00349     set nodeset {}
00350     foreach node $nodes {
00351         set descendants [$self do_subtree $node]
00352         lappend nodeset {*}$descendants
00353     }
00354     set nodes $nodeset
00355     return $args
00356     }
00357 
00358     /*  generates all nodes in the tree*/
00359     ret  tree (type args) {
00360     set nodes [$self do_subtree [$tree rootname]]
00361     return $args
00362     }
00363 
00364     /*  generates all subtrees rooted at node*/
00365     /* method descendants {args} {*/
00366     /*  set nodes [$tree apply descendants]*/
00367     /*  return $args*/
00368     /* }*/
00369 
00370     /*  flattened next subtrees*/
00371     ret  forward (type args) {
00372     set nodes [$self applyself do_next*]    ;# next siblings
00373     $self descendants   ;# their proper descendants
00374     return $args
00375     }
00376 
00377     /*  synonym for [forward]*/
00378     ret  later (type args) {
00379     $self forward
00380     return $args
00381     }
00382 
00383     /*  flattened previous subtrees in tree order*/
00384     ret  earlier (type args) {
00385     set nodes [$self applyself do_previous*]    ;# all earlier siblings
00386     $self descendants   ;# their proper descendants
00387     return $args
00388     }
00389 
00390     /*  flattened previous subtrees in reverse tree order*/
00391     /*  FIXME - this isn't going to return things in the correct order*/
00392     ret  backward (type args) {
00393     set nodes [$self applyself do_previous*]    ;# all earlier siblings
00394     $self subtree   ;# their subtrees
00395     set nodes [::struct::list reverse $nodes]   ;# reverse order
00396     return $args
00397     }
00398 
00399     /*  Returns the node type of nodes*/
00400     ret  nodetype () {
00401     set nodes [$self apply get @type]
00402     return {}   ;# terminate query
00403     }
00404 
00405     /*  Reduce to nodes of @type $t*/
00406     ret  oftype (type t , type args) {
00407     return [$self stringP [list equal -nocase $t] @type {*}$args]
00408     }
00409 
00410     /*  Reduce to nodes not of @type $t*/
00411     ret  nottype (type t , type args) {
00412     return [$self stringNP [list equal -nocase $t] @type {*}$args]
00413     }
00414 
00415     /*  Reduce to nodes whose @type is one of $attrs*/
00416     /*  @type values are assumed to be simple strings*/
00417     ret  oftypes (type attrs , type args) {
00418     set n {}
00419     foreach result [$self mapself do_attr list @type] node $nodes {
00420         if {[lsearch $attrs $result] > -1} {
00421         #puts stderr "$self oftypes '$attrs' -> $result - $node"
00422         lappend n $node
00423         }
00424     }
00425     set nodes $n
00426     return $args
00427     }
00428 
00429     /*  Reduce to nodes with attribute $attr (can be a glob)*/
00430     ret  hasatt (type attr , type args) {
00431     set nodes [$self bool keyexists $attr]
00432     return $args
00433     }
00434 
00435     /*  Returns values of attribute attname*/
00436     ret  attval (type attname) {
00437     $self hasatt $attname   ;# only nodes with attribute
00438     set nodes [$self apply get $attname]    ;# get the attribute nodes
00439     return {}   ;# terminate query
00440     }
00441 
00442     /*  Reduce to nodes with attribute $attr of $value*/
00443     ret  withatt (type attr , type value , type args) {
00444     $self hasatt $attr  ;# only nodes with attribute
00445     return [$self stringP [list equal -nocase $value] $attr {*}$args]
00446     }
00447 
00448     /*  Reduce to nodes with attribute $attr of $value*/
00449     ret  withatt! (type attr , type val , type args) {
00450     return [$self stringP [list equal $val] $attr {*}$args]
00451     }
00452 
00453     /*  Reduce to nodes with attribute $attr value one of $vals*/
00454     ret  attof (type attr , type vals , type args) {
00455 
00456     set result {}
00457     foreach node $nodes {
00458         set x [string tolower [[$self treeObj] get $node $attr]]
00459         if {[lsearch $vals $x] != -1} {
00460         lappend result $node
00461         }
00462     }
00463 
00464     set nodes $result
00465     return $args
00466     }
00467 
00468     /*  Reduce to nodes whose attribute $attr string matches $match*/
00469     ret  attmatch (type attr , type match , type args) {
00470     $self stringP [list match {*}$match] $attr
00471     return $args
00472     }
00473 
00474     /*  Side Effect: set attribute $attr to $val*/
00475     ret  set (type attr , type val , type args) {
00476     $self apply set $attr $val
00477     return $args
00478     }
00479 
00480     /*  Side Effect: unset attribute $attr*/
00481     ret  unset (type attr , type args) {
00482     $self apply unset $attr
00483     return $args
00484     }
00485 
00486     /*  apply string operation $op to attribute $attr on each node*/
00487     ret  string (type op , type attr) {
00488     set nodes [$self mapself do_attr [list string {*}$op] $attr]
00489     return {}   ;# terminate query
00490     }
00491 
00492     /*  remove duplicate nodes, preserving order*/
00493     ret  unique (type args) {
00494     set all {}
00495     array set keys {}
00496     foreach node $nodes {
00497         if {![info exists keys($node)]} {
00498         set keys($node) 1
00499         lappend all $node
00500         }
00501     }
00502     set nodes $all
00503     return $args
00504     }
00505 
00506     /*  construct the set of nodes present in both $nodes and node set $and*/
00507     ret  and (type and , type args) {
00508     set nodes [::struct::set intersect $and $nodes]
00509     return $args
00510     }
00511 
00512     /*  return result of new query $query, preserving current node set*/
00513     ret  subquery (type args) {
00514     set org $nodes  ;# save current node set
00515     set new [uplevel 1 [list $query query {*}$args]]
00516     set nodes $org  ;# restore old node set
00517 
00518     return $new
00519     }
00520 
00521     /*  perform a subquery and and in the result*/
00522     ret  andq (type q , type args) {
00523     $self and [uplevel 1 [list $self subquery {*}$q]]
00524     return $args
00525     }
00526 
00527     /*  construct the set of nodes present in $nodes or node set $or*/
00528     ret  or (type or , type args) {
00529     set nodes [::struct::set union $nodes $or]
00530     $self unique
00531     return $args
00532     }
00533 
00534     /*  perform a subquery and or in the result*/
00535     ret  orq (type q , type args) {
00536     $self or [uplevel 1 [list $self subquery {*}$q]]
00537     return $args
00538     }
00539 
00540     /*  construct the set of nodes present in $nodes but not node set $not*/
00541     ret  not (type not , type args) {
00542     set nodes [::struct::set difference $nodes $not]
00543     return $args
00544     }
00545 
00546     /*  perform a subquery and return the set of nodes not in the result*/
00547     ret  notq (type q , type args) {
00548     $self not [uplevel 1 [list $self subquery {*}$q]]
00549     return $args
00550     }
00551 
00552     /*  select the first of the nodes*/
00553     ret  select (type args) {
00554     set nodes [lindex $nodes 0]
00555     return $args
00556     }
00557 
00558     /*  perform a subquery then replace the nodeset*/
00559     ret  transform (type q , type var , type body , type args) {
00560     upvar 1 $var iter
00561     set new {}
00562     foreach n [uplevel 1 [list $self subquery {*}$q]] {
00563         set iter $n
00564         switch -exact -- [catch {
00565         uplevel 1 $body
00566         } result eo] {
00567         0 {
00568             # ok
00569             lappend new $result
00570         }
00571         1 {
00572             # pass errors up
00573             return -code error $result
00574         }
00575         2 {
00576             # return
00577             set nodes $result
00578             return
00579         }
00580         3 {
00581             # break
00582             break
00583         }
00584         4 {
00585             # continue
00586             continue
00587         }
00588         }
00589     }
00590 
00591     set nodes $new
00592 
00593     return $args
00594     }
00595 
00596     /*  replace the nodeset*/
00597     ret  map (type var , type body , type args) {
00598     upvar 1 $var iter
00599     set new {}
00600     foreach n $nodes {
00601         set iter $n
00602         switch -exact -- [catch {
00603         uplevel 1 $body
00604         } result eo] {
00605         0 {
00606             # ok
00607             lappend new $result
00608         }
00609         1 {
00610             # pass errors up
00611             return -code error $result
00612         }
00613         2 {
00614             # return
00615             set nodes $result
00616             return
00617         }
00618         3 {
00619             # break
00620             break
00621         }
00622         4 {
00623             # continue
00624             continue
00625         }
00626         }
00627     }
00628 
00629     set nodes $new
00630 
00631     return $args
00632     }
00633 
00634     /*  perform a subquery $query then map $body over results*/
00635     ret  foreach (type q , type var , type body , type args) {
00636     upvar 1 $var iter
00637     foreach n [uplevel 1 [list $self subquery {*}$q]] {
00638         set iter $n
00639         uplevel 1 $body
00640     }
00641     return $args
00642     }
00643 
00644     /*  perform a query, then evaluate $body*/
00645     ret  with (type q , type body , type args) {
00646     # save current node set, implied reset
00647     set org $nodes; set nodes {}
00648 
00649     uplevel 1 [list $self query {*}$q]
00650     set result [uplevel 1 $body]
00651 
00652     # restore old node set
00653     set new $nodes; set nodes $org
00654 
00655     return $args
00656     }
00657 
00658     /*  map $body over $nodes*/
00659     ret  over (type var , type body , type args) {
00660     upvar 1 $var iter
00661     set result {}
00662     foreach n $nodes {
00663         set iter $n
00664         uplevel 1 $body
00665     }
00666     return $args
00667     }
00668 
00669     /*  perform the query*/
00670     ret  query (type args) {
00671     # iterate over the args, treating each as a method invocation
00672     while {$args != {}} {
00673         #puts stderr "query $self $args"
00674         set args [uplevel 1 [list $query {*}$args]]
00675         #puts stderr "-> $nodes"
00676     }
00677 
00678     return $nodes
00679     }
00680 
00681     /*  append the literal $val to node set*/
00682     ret  quote (type val , type args) {
00683     lappend nodes $val
00684     return $args
00685     }
00686 
00687     /*  replace the node set with the literal*/
00688     ret  replace (type val , type args) {
00689     set nodes $val
00690     return $args
00691     }
00692 
00693     /*  set nodeset to empty*/
00694     ret  reset (type args) {
00695     set nodes {}
00696     return $args
00697     }
00698 
00699     /*  delete all nodes in node set*/
00700     ret  delete (type args) {
00701 
00702     foreach node $nodes {
00703         $tree cut $node
00704     }
00705 
00706     set nodes {}
00707     return $args
00708     }
00709 
00710     /*  return the node set*/
00711     ret  result () {
00712     return $nodes
00713     }
00714 
00715     constructor {args} {
00716      query =  [from args -query ""]
00717     if {$query == ""} {
00718          query =  $self
00719     }
00720 
00721      nodes =  [from args -nodes {}]
00722 
00723      tree =  [from args -tree ""]
00724 
00725     uplevel 1 [list $self query {*}$args]
00726     }
00727 
00728     /*  Return result, and destroy this query*/
00729     /*  useful in constructing a sub-query*/
00730     ret  discard (type args) {
00731     return [K [$self result] [$self destroy]]
00732     }
00733 
00734     ret  K (type x , type y) {
00735     set x
00736     }
00737 }
00738 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1