| 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) | 
|---|