#!/usr/local/bin/perl # $Id: cwb2daVinci,v 1.13 1997/10/07 17:08:41 pxs Exp $ # This script is part of an experimental interface between the CWB and # the graph drawing package daVinci from Bremen. It takes CWB output # in various forms and turns it into a daVinci command to display the # transistions or graphs. # # There are no newlines or spaces in the output because daVinci can't # cope with them. # # All sorts of problems can cause nodes not to be recognised as the # same when "in fact" they are the same. Most are outwith our control # here. We do, however, delete spaces with gay abandon to ease the problem. # # Using the -a option we can take the existing graph (stored in a DBM # file) and add the # transitions (or graph) from a new agent into it, instead of starting # from scratch. DaVinci will still insist on starting from scratch, # re-reading the whole graph, but that's its problem! # use Getopt::Std; getopts('af:t:i:'); # f(ormat commun) option takes an fc file # t(ransistions) from the opt. agent as argument # a(dd) this info to existing graph, don't replace # i(input) from this arg, not from STDIN $opt_a && ($dontwarn = 1); # if we're adding things may well be # disconnected; don't warn about that # if -i was used, get INPUT from there, otherwise use STDIN open (INPUT, $opt_i ? "echo \"$opt_i\"|" : "-"); $errorfile = "/tmp/CWBtranslateerrors"; $tmpfile = "/tmp/CWBstoredinfo"; # used to store this graph in case we # have to add to it next time # it would be more efficient to store colour info in the same array as # the transition info, but daVinci is so much the limiting factor on # performance that I'm doing it this way for programmer efficiency! $colourfile = "/tmp/CWBcolourinfo"; # used to store colour info $prompt = "Command:"; $edgeattributes1 = "[a(\"EDGECOLOR\",\"black\"),a(\"_DIR\",\"none\")]"; $edgeattributes2 = "[a(\"EDGECOLOR\",\"black\")]"; # with an arrow $edgestring = "\"anything\""; $nodestring = "\"anything\""; $topnodekey = 'CWB TOP NODE'; # hack: store topnode in the DBM file open (ERRORS, ">>$errorfile") || die $!; # If required, read in the transition system for the current graph. # Otherwise, delete the stored info and start again. unless ($opt_a) { unlink <$tmpfile.*>; unlink <$colourfile.*>;} dbmopen (%transitions, $tmpfile, 0666); dbmopen (%colour, $colourfile, 0666); #$topnode = $transitions{$topnodekey}; #delete $transitions{$topnodekey}; # it doesn't belong, it was a hack to # put it there! # Set up the transition system... if ($opt_f) { &readfc($opt_f); } elsif ($opt_t) { # HACK. We store the topnode in the DBM array, so if it an entry for it # already exists there we should use it. If it's not there, get it, and # we'll put it into the array at the end. unless ($topnode) { if ($opt_t =~ /lookinregion/ ) {&gettopnode;} else {$topnode = $opt_t;} $topnode =~ tr/ //d; } &getarrowtransitions; $colour{$topnode} = 'y'; } else {die "No option given";} # ... and now make a daVinci term out of it &doit; # check that we used all the information -- if not, we probably have an alias # problem, or a prefix-form problem, or... for (keys %transitions) { unless ($seen{$_}) { ($othertree = &graphtree($_)) =~ s/\\/\\\\/g; print ",$othertree"; s/\\/\\\\/g; push (@unseen, $_); } } print "])"; # close the list of graphtrees # Make sure the node the user's really interested in is visible $topnode =~ s/\\/\\\\/g; #escape backslashes for daVinci print "\nfocus_node_animated (30, \"$topnode\")"; if ((!$dontwarn) && @unseen) { print "\nconfirmer(\"WARNING\",[\"Node(s) ", join (',',@unseen), " not connected. \",\"This could be an alias problem.\"],\"OK\")"; } exit; sub gettopnode { while () { next if /^\s*$/; if (/\s*(transitions|graph)\s*\(?([^;\n]*)\)?;?\s*$/) { $topnode = $2; last; } } } sub getarrowtransitions { local ($node) = $topnode; while () {#print "\nLine is $_, node is $node\n"; chop; next if /^\s*$/; if (/--- (.*) ---> (.*)/) { unless ($colour{$node}) { # we don't already know all about transitions from here, and this line # gives us one, so grab it. ($action = $1) =~ tr/ //d; ($agent = $2) =~ tr/ //d; $transitions{$node} .= "$action -> $agent\n"; } } elsif (/None/) {next; } else { # we've found the next node. Colour the old one green and grab this next one. ($node eq $topnode) || ($colour{$node} = 'g'); ($node) = /\s*(.*)\s*/; $node =~ tr/ //d; } } # remember to colour the final node too, groan! $colour{$node} = 'g'; } sub readfc { local ($file) = @_; open (FILE, $file) || die $!; $state = 0; while () { ($topnode = "st_0") if /automaton "(.*)"/; # want all names, really next unless defined $topnode; $gotallatoms++ if /^state 0/; if (!$gotallatoms) { /^(\d*):\s*"(.*)"/ && ($atom{$1} = $2); } elsif (/^state/) { $node = $atom{$state}; $state++; } elsif (/\d*:\s*a(\w*) -> (\w*)/) { ($action = $atom{$1}) || warn "No action for $1"; ($agent = $atom{$2}) || warn "No agent for $2"; $transitions{$node} .= "$action -> $agent\n"; # print "Node $node action $action agent $agent\n"; } elsif (/^end/) { # do nothing } else {print ERRORS "Don't recognise $_";} } } sub doit { ($tree = &graphtree($topnode)) =~ s/\\/\\\\/g;#escape backslashes for daVinci print "new_term_placed([", $tree;# don't close brackets, there may be more } # If we've seen an agent before, we want a reference to it, otherwise, we # make a new node out of it. NB This really will build the whole graph # (i.e. using all the transition info we have) from this node, because # we call edges which calls edge which calls graphtree... sub graphtree { local ($node) = @_; local ($graphtreedesc) = ""; if ($seen{$node}++) { $graphtreedesc = "r(\"$node\")"; } else { $colour = &lookupcolour($node); $graphtreedesc = "l(\"$node\",n($nodestring,[a(\"COLOR\",\"$colour\"),a(\"OBJECT\",\"$node\")],"; $graphtreedesc .= &edges($node); $graphtreedesc .= "))"; # one for the l, one for the n } #print "graphtree on $node gave $graphtreedesc\n"; $graphtreedesc; } sub lookupcolour { local ($node) = @_; local ($colour) = $colour{$node}; local ($return); if ($colour eq 'y') { $return = 'yellow'; # make it green for next time $colour{$node} = 'g'; } elsif ($colour eq 'g') { $return = 'green'; } else { $return = 'white'; } $return; } # builds a daVinci representation of the transitions from the given agent sub edges { local ($node) = @_; local ($edgesdesc) = ""; local(@nextsteps, @parts); @nextsteps = split (/\n/, $transitions{$node});#print "Size $#nextsteps\n"; for (@nextsteps) { ($action, $agent) = /(.*) -> (.*)/; #print "\n Got $node -> $action -> $agent\n"; push (@parts, &edge($node, $action, $agent)); } $edgesdesc = "[".join(',',@parts)."]"; #print "edges on $node gave $edgesdesc\n"; $edgesdesc; } # This gives an "edge" which actually includes a dummy node in the middle # to carry the edge label. NB It also gives the graph at the bottom of # the edge, calling graphtree on the bottom, and so on! sub edge { local ($top, $action, $bottom) = @_; local ($edgedesc) = ""; $edgedesc = "l(\"edge$top->$action->$bottom\",e($edgestring,$edgeattributes1,l(\"DUMMY$top->$action->$bottom\",n($nodestring,[a(\"COLOR\",\"white\"),a(\"OBJECT\",\"$action\"),a(\"_GO\",\"text\")],[l(\"edge$bottom<-$action<-$top\",e($edgestring,$edgeattributes2,"; $edgedesc .= &graphtree($bottom); $edgedesc .= "))]))))"; # you counted 'em out, now count 'em in! #print "edge on $top $action $bottom gave $edgedesc\n"; $edgedesc; }