| 1 | #!/usr/bin/ruby -I /usr/local/lib/utt -I $HOME/.local/lib/utt | 
|---|
| 2 |  | 
|---|
| 3 | $: << "#{ENV['HOME']}/.local/lib/utt" | 
|---|
| 4 | $: << "/usr/local/lib/utt" | 
|---|
| 5 |  | 
|---|
| 6 | require 'getoptlong' | 
|---|
| 7 | require 'seg.rb' | 
|---|
| 8 |  | 
|---|
| 9 | opts = GetoptLong.new( | 
|---|
| 10 | [ '--help',     '-h',   GetoptLong::NO_ARGUMENT ], | 
|---|
| 11 | [ '--debug',    '-d',   GetoptLong::NO_ARGUMENT ], | 
|---|
| 12 | [ '--format',   '-F',   GetoptLong::REQUIRED_ARGUMENT ], | 
|---|
| 13 | [ '--info',     '-I',   GetoptLong::REQUIRED_ARGUMENT ], | 
|---|
| 14 | [ '--only-trees','-t',  GetoptLong::NO_ARGUMENT ]) | 
|---|
| 15 |  | 
|---|
| 16 | $helptext= | 
|---|
| 17 | "The program generates trees from the graph output by dgp. dgp must\n"+ | 
|---|
| 18 | "must be run with '-i ds' option.\n\n"+ | 
|---|
| 19 | "Command:       tre [options]\n\n"+ | 
|---|
| 20 | "Options:\n"+ | 
|---|
| 21 | "--help         -h      Print help (this text) and exit.\n"+ | 
|---|
| 22 | "--debug        -d      Verbose output. For developers only.\n"+ | 
|---|
| 23 | "--format=s     -F s    Output format. Recognized values:\n"+ | 
|---|
| 24 | "                               a       root + list of arcs\n"+ | 
|---|
| 25 | "                               p       parenthesized notation\n"+ | 
|---|
| 26 | "                               h       human readable indented tree format\n"+ | 
|---|
| 27 | "                       Multiple values are allowed. (default p)\n"+ | 
|---|
| 28 | "--info=s       -I s    Information printed. Recognized values:\n"+ | 
|---|
| 29 | "                               n       node identifier\n"+ | 
|---|
| 30 | "                               f       surface form\n"+ | 
|---|
| 31 | "                               m       morphological information\n"+ | 
|---|
| 32 | "                               l       arc labels\n"+ | 
|---|
| 33 | "--only-trees   -t      Do not copy input. Print trees only.\n" | 
|---|
| 34 |  | 
|---|
| 35 | $DEBUG=false | 
|---|
| 36 | $FORMAT='p' | 
|---|
| 37 | $INFO='DEFAULT' | 
|---|
| 38 | $ONLYTREES=false | 
|---|
| 39 |  | 
|---|
| 40 | opts.each do |opt, arg| | 
|---|
| 41 |   case opt | 
|---|
| 42 |   when '--help' | 
|---|
| 43 |     print $helptext | 
|---|
| 44 |     exit 0 | 
|---|
| 45 |   when '--debug' | 
|---|
| 46 |     $DEBUG=true | 
|---|
| 47 |   when '--format' | 
|---|
| 48 |     $FORMAT=arg | 
|---|
| 49 |   when '--info' | 
|---|
| 50 |     $INFO=arg | 
|---|
| 51 |   when '--only-trees' | 
|---|
| 52 |     $ONLYTREES=true | 
|---|
| 53 |   else | 
|---|
| 54 |     print "Unknown option #{opt}. Ignored.\n" | 
|---|
| 55 |   end | 
|---|
| 56 | end | 
|---|
| 57 |  | 
|---|
| 58 | if $INFO=='DEFAULT' | 
|---|
| 59 |   case $FORMAT | 
|---|
| 60 |     when 'p','a' | 
|---|
| 61 |     $INFO='nl' | 
|---|
| 62 |     when 'h' | 
|---|
| 63 |     $INFO='fmnl' | 
|---|
| 64 |   end | 
|---|
| 65 | end | 
|---|
| 66 |  | 
|---|
| 67 | $dgpsep=';' | 
|---|
| 68 |  | 
|---|
| 69 | def tre(input) | 
|---|
| 70 |   $gphid=[] | 
|---|
| 71 |   $form=[] | 
|---|
| 72 |   $lem=[] | 
|---|
| 73 |   nodes=[] | 
|---|
| 74 |   count=0 | 
|---|
| 75 |   seg=Seg.new | 
|---|
| 76 |   for line in input | 
|---|
| 77 |     print line unless $ONLYTREES | 
|---|
| 78 |     seg.set(line) | 
|---|
| 79 |     if dgp=seg['dgp'] | 
|---|
| 80 |       if nodes==[] && seg[3]!='BOS' | 
|---|
| 81 |         print "A sentence must start with BOS segment. Aborting.\n" | 
|---|
| 82 |         return | 
|---|
| 83 |       end | 
|---|
| 84 |  | 
|---|
| 85 |       id=dgp[/^\d+/].to_i | 
|---|
| 86 |  | 
|---|
| 87 |       if gph=seg['gph'] | 
|---|
| 88 |         $gphid[id]=gph[/^\d+/].to_i | 
|---|
| 89 |       else | 
|---|
| 90 |         print "No gph field. Aborting.\n" | 
|---|
| 91 |         return | 
|---|
| 92 |       end | 
|---|
| 93 |  | 
|---|
| 94 |       $form[$gphid[id]]=seg[4] | 
|---|
| 95 |       $lem[$gphid[id]]=seg['lem'] | 
|---|
| 96 |                | 
|---|
| 97 |       nodes[id] = [seg[1].to_i,dgp] | 
|---|
| 98 |  | 
|---|
| 99 |       if seg[3]=='EOS' | 
|---|
| 100 |         $pref = "#{seg[1]} #{seg[2]} SYN *" | 
|---|
| 101 |         parsegraph(nodes) | 
|---|
| 102 |         printgraph if $DEBUG | 
|---|
| 103 |         $thetrees=[] | 
|---|
| 104 |         gentrees2 | 
|---|
| 105 |         for t in $thetrees | 
|---|
| 106 |           count += 1 | 
|---|
| 107 |           t1=ground(t) | 
|---|
| 108 |           case $FORMAT | 
|---|
| 109 |           when /a/ | 
|---|
| 110 |             print "#{$pref} tre:#{count} arc:" | 
|---|
| 111 |             printarcs(t1[0],t1[1]) | 
|---|
| 112 |             print "\n" | 
|---|
| 113 |           when /p/ | 
|---|
| 114 |             print "#{$pref} tre:#{count} par:" | 
|---|
| 115 |             printpar(t1[0],t1[1]) | 
|---|
| 116 |             print "\n" | 
|---|
| 117 |           when /h/ | 
|---|
| 118 |             print "#\n# tree #{count}\n# ------\n" | 
|---|
| 119 |             printtree(t1[0],t1[1],0) | 
|---|
| 120 |           end | 
|---|
| 121 |         end | 
|---|
| 122 |         nodes=[] | 
|---|
| 123 |       end | 
|---|
| 124 |     end | 
|---|
| 125 |   end | 
|---|
| 126 | end | 
|---|
| 127 |  | 
|---|
| 128 |  | 
|---|
| 129 | def nodeinfo(id) | 
|---|
| 130 |   info="" | 
|---|
| 131 |   if $INFO =~ /n/ | 
|---|
| 132 |     info += id.to_s                            | 
|---|
| 133 |     info += '.' if $INFO =~ /[fm]/ | 
|---|
| 134 |   end | 
|---|
| 135 |   if $INFO =~ /f/ | 
|---|
| 136 |     info += $form[id]  | 
|---|
| 137 |     info += ';' if $INFO =~ /m/ | 
|---|
| 138 |   end | 
|---|
| 139 |   if $INFO =~ /m/ | 
|---|
| 140 |     info += $lem[id]   | 
|---|
| 141 |   end | 
|---|
| 142 |   info | 
|---|
| 143 | end | 
|---|
| 144 |  | 
|---|
| 145 |  | 
|---|
| 146 | def printarcs(root,arcs) | 
|---|
| 147 |   print nodeinfo(root) | 
|---|
| 148 |   for a in arcs | 
|---|
| 149 |     print ';' | 
|---|
| 150 |     print "#{a[2]}:" if $INFO =~ /l/ | 
|---|
| 151 |       print nodeinfo(a[0])+'-'+nodeinfo(a[1]) | 
|---|
| 152 |   end | 
|---|
| 153 | end | 
|---|
| 154 |  | 
|---|
| 155 | def printtree(root,arcs,o) | 
|---|
| 156 |   if o==0 | 
|---|
| 157 |         print "# %-16s" % "root: " | 
|---|
| 158 |   end | 
|---|
| 159 |   print nodeinfo(root),"\n" | 
|---|
| 160 |   for arc in arcs.select{ |a| a[0]==root }.sort{|a,b| a[1]<=>b[1] } | 
|---|
| 161 |     print '# ',"   "*(o+1) | 
|---|
| 162 |     print "%-16s" % (arc[2]+": ") | 
|---|
| 163 |     printtree(arc[1],arcs,o+1) | 
|---|
| 164 |   end | 
|---|
| 165 | end | 
|---|
| 166 |  | 
|---|
| 167 | def printpar(root,arcs) | 
|---|
| 168 |   print nodeinfo(root) | 
|---|
| 169 |   deps = arcs.select{ |a| a[0]==root }.sort{|a,b| a[1]<=>b[1] } | 
|---|
| 170 |   unless deps == [] | 
|---|
| 171 |     print '(' | 
|---|
| 172 |     cont=false | 
|---|
| 173 |     for arc in deps | 
|---|
| 174 |       if cont then print ',' else cont=true end | 
|---|
| 175 |       print arc[2],':' if $INFO =~ /l/ | 
|---|
| 176 |       printpar(arc[1],arcs) | 
|---|
| 177 |     end | 
|---|
| 178 |     print ')' | 
|---|
| 179 |   end | 
|---|
| 180 | end | 
|---|
| 181 |  | 
|---|
| 182 |  | 
|---|
| 183 | def parsegraph(nodes) | 
|---|
| 184 |  | 
|---|
| 185 |   $n   =nodes.length | 
|---|
| 186 |   $sat =[]; | 
|---|
| 187 |  | 
|---|
| 188 |   $vis =[]; | 
|---|
| 189 |   $succ=[]; | 
|---|
| 190 |   $lhs =[]; | 
|---|
| 191 |   $arcs=[]; | 
|---|
| 192 |   $pos=[] | 
|---|
| 193 |  | 
|---|
| 194 |   for dgp in nodes | 
|---|
| 195 |  | 
|---|
| 196 |     parts  = dgp[1].split($dgpsep,6) | 
|---|
| 197 |  | 
|---|
| 198 |     if parts[3]==nil || parts[4]==nil || parts[5]==nil | 
|---|
| 199 |       $stderr.print "ERR: tre requires dgp be called with '--info s' option. Aborting.\n" | 
|---|
| 200 |       exit | 
|---|
| 201 |     end | 
|---|
| 202 |  | 
|---|
| 203 |     i      = parts[0].to_i | 
|---|
| 204 |     $pos[i] = dgp[0].to_i | 
|---|
| 205 |     $sat << i if parts[1]=="s" | 
|---|
| 206 |     $arcs |= parts[2].split(',').map{ |a| case a  | 
|---|
| 207 |                                           when /\-\-(\w+)-(\d+)\/(\d+)/ | 
|---|
| 208 |                                             [i, $2.to_i, $1, $3.to_i] | 
|---|
| 209 |                                           when /\+\+(\d+)-(\w+)\/(\d+)/ | 
|---|
| 210 |                                             [$1.to_i, i, $2, $3.to_i] | 
|---|
| 211 |                                           end } | 
|---|
| 212 |     $succ |= parts[3][1..-2].split(',').map{|x| [x.to_i,i]} | 
|---|
| 213 |     $vis  |= parts[4][1..-2].split(',').map{|x| [x.to_i,i]}  | 
|---|
| 214 |     $lhs  |= parts[5][1..-2].split(',').map{|x| [x.to_i,i]} + [[i,i]] | 
|---|
| 215 |  | 
|---|
| 216 |   end | 
|---|
| 217 | end | 
|---|
| 218 |  | 
|---|
| 219 |  | 
|---|
| 220 | def ground(t) | 
|---|
| 221 |   [ $gphid[t[0]] , t[1].map{|a| [$gphid[a[0]],$gphid[a[1]],a[2]]} ] | 
|---|
| 222 | end   | 
|---|
| 223 |  | 
|---|
| 224 |  | 
|---|
| 225 | def gentrees2() | 
|---|
| 226 |   $thetrees=[]; | 
|---|
| 227 |   bos=0; eos=$n-1; | 
|---|
| 228 |   roots = (1...eos).select{|i| $vis.include? [i,eos]}.select{|i| $vis.include? [bos,i]} | 
|---|
| 229 |   if $DEBUG then print "ROOTS: #{roots.inspect}\n" end | 
|---|
| 230 |   for i in roots | 
|---|
| 231 |     $theroot=i | 
|---|
| 232 |     for r in buildR(i , eos, []) | 
|---|
| 233 |       (rmin,rmax,rtree) = r | 
|---|
| 234 |       buildR(bos, rmin, rtree) | 
|---|
| 235 |     end | 
|---|
| 236 |   end | 
|---|
| 237 | end | 
|---|
| 238 |  | 
|---|
| 239 |  | 
|---|
| 240 | def buildR(min, max, tree) | 
|---|
| 241 |   if $DEBUG then print "buildR--#{min}--#{max}--#{tree.inspect}\n" end | 
|---|
| 242 |   trees=[] | 
|---|
| 243 |   for a in $arcs.select{|a| a[0]==max && $vis.include?([min,a[1]]) } | 
|---|
| 244 |     if $DEBUG then print "ARC: #{a.inspect}\n" end | 
|---|
| 245 |     for r in buildR(a[1],a[3],tree+[a]) | 
|---|
| 246 |       (rmin,rmax,rarcs) = r | 
|---|
| 247 |       for l in buildR(min,rmin,rarcs) | 
|---|
| 248 |         (lmin,lmax,larcs) = l | 
|---|
| 249 |         trees << [lmin,rmax,larcs] | 
|---|
| 250 |       end | 
|---|
| 251 |     end | 
|---|
| 252 |   end | 
|---|
| 253 |   for i in (0...$n).select{|i| $succ.include?([i,max])}.select{|i| $lhs.include?([min,i])} | 
|---|
| 254 |     for l in buildL(min,i,tree) | 
|---|
| 255 |       (lmin,lmax,larcs) = l | 
|---|
| 256 |       trees << [lmin,lmax,larcs] | 
|---|
| 257 |     end | 
|---|
| 258 |   end | 
|---|
| 259 |   trees   | 
|---|
| 260 | end | 
|---|
| 261 |      | 
|---|
| 262 |  | 
|---|
| 263 | def buildL(min,max,tree) | 
|---|
| 264 |   if $DEBUG then print "buildL--#{min}--#{max}--#{tree.inspect}\n" end | 
|---|
| 265 |   if $pos[min]==$pos[max] | 
|---|
| 266 |     if min==0 && max==0 | 
|---|
| 267 |       $thetrees.push [$theroot,tree] | 
|---|
| 268 |       if $DEBUG then print "adding tree: #{tree.inspect}\n" end | 
|---|
| 269 |     end | 
|---|
| 270 |     return [[max,max,tree]] | 
|---|
| 271 |   end | 
|---|
| 272 |   trees=[] | 
|---|
| 273 |   for arc in $arcs.select{|a| a[1]==max && $lhs.include?([min,a[0]]) } | 
|---|
| 274 |     if $DEBUG then print "ARC: #{arc.inspect}\n" end | 
|---|
| 275 |     for r in buildR(arc[3],max,tree+[arc]) | 
|---|
| 276 |       (rmin,rmax,rarcs) = r | 
|---|
| 277 |       for l in buildL(min,rmin,rarcs) | 
|---|
| 278 |         (lmin,lmax,larcs) = l | 
|---|
| 279 |         trees << [lmin,lmax,larcs] | 
|---|
| 280 |       end | 
|---|
| 281 |     end | 
|---|
| 282 |   end | 
|---|
| 283 |   trees | 
|---|
| 284 | end | 
|---|
| 285 |  | 
|---|
| 286 |  | 
|---|
| 287 | def printgraph() | 
|---|
| 288 |    | 
|---|
| 289 |   print "N:    #{$n}\n" | 
|---|
| 290 |   print "SAT:  #{set_to_s($sat)}\n" | 
|---|
| 291 |   print "SUCC: #{rel_to_s($succ)}\n" | 
|---|
| 292 |   print "VIS:  #{rel_to_s($vis)}\n" | 
|---|
| 293 |   print "LHS:  #{rel_to_s($lhs)}\n" | 
|---|
| 294 |   print "ARCS: #{arcs_to_s($arcs)}\n" | 
|---|
| 295 | end | 
|---|
| 296 |  | 
|---|
| 297 | def set_to_s(s) "{#{s.join(',')}}" end | 
|---|
| 298 | def rel_to_s(r) "{#{r.map{|p| "(#{p[0]},#{p[1]})"}.join(',')}}" end | 
|---|
| 299 | def arc_to_s(q) "-#{q[0]}-#{q[2]}-#{q[1]}/#{q[3]}" end | 
|---|
| 300 | def arcs_to_s(a) "{#{a.map{|q| arc_to_s(q)}.join(',')}}" end | 
|---|
| 301 |  | 
|---|
| 302 | ###################################################################### | 
|---|
| 303 |  | 
|---|
| 304 | tre($stdin) | 
|---|