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

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1