| 1 | #!/usr/bin/perl |
|---|
| 2 | |
|---|
| 3 | #package: UAM Text Tools |
|---|
| 4 | #component: dgc (dg compiler) |
|---|
| 5 | #version: 1.0 |
|---|
| 6 | #author: Tomasz Obrebski |
|---|
| 7 | |
|---|
| 8 | use lib "/usr/local/lib/utt"; |
|---|
| 9 | use lib "$ENV{'HOME'}/.local/lib/utt"; |
|---|
| 10 | |
|---|
| 11 | #use strict; |
|---|
| 12 | use Getopt::Long; |
|---|
| 13 | use Data::Dumper; |
|---|
| 14 | use attr; |
|---|
| 15 | use File::HomeDir; |
|---|
| 16 | use Parse::RecDescent; |
|---|
| 17 | |
|---|
| 18 | $::RD_HINT=1; |
|---|
| 19 | # use List::MoreUtils; |
|---|
| 20 | |
|---|
| 21 | my $systemconfigfile='/etc/utt/dgc.conf'; |
|---|
| 22 | my $userconfigfile=0; #home()."/.utt/dgc.conf"; |
|---|
| 23 | |
|---|
| 24 | Getopt::Long::Configure('no_ignore_case_always'); |
|---|
| 25 | |
|---|
| 26 | my $help=0; |
|---|
| 27 | my $catfile=0; |
|---|
| 28 | my $dicfile=0; |
|---|
| 29 | my $gramfile=0; |
|---|
| 30 | my $outputfile=0; |
|---|
| 31 | |
|---|
| 32 | #read configuration files########################### |
|---|
| 33 | my $file; |
|---|
| 34 | foreach $file ($systemconfigfile, $userconfigfile){ |
|---|
| 35 | if(open(CONFIG, $file)){ |
|---|
| 36 | while (<CONFIG>) { |
|---|
| 37 | chomp; s/#.*//; s/^\s+//; s/\s+$//; |
|---|
| 38 | next unless length; |
|---|
| 39 | my ($name, $value) = split(/\s*=\s*/, $_, 2); |
|---|
| 40 | if(($name eq "categories")or($name eq "c")) { $catfile=$value; } |
|---|
| 41 | elsif(($name eq "dictionary")or($name eq "d")) { $dicfile=$value; } |
|---|
| 42 | elsif(($name eq "grammar")or($name eq "g")) { $gramfile=$value; } |
|---|
| 43 | elsif(($name eq "outputfile")or($name eq "o")) { $outputfile=$value; } |
|---|
| 44 | elsif(($name eq "help")or($name eq "h")) { $help=1; } |
|---|
| 45 | |
|---|
| 46 | } |
|---|
| 47 | close CONFIG; |
|---|
| 48 | } |
|---|
| 49 | } |
|---|
| 50 | ######################################################### |
|---|
| 51 | |
|---|
| 52 | GetOptions("help|h" => \$help, |
|---|
| 53 | "categories|c=s" => \$catfile, |
|---|
| 54 | "dictionary|d=s" => \$dicfile, |
|---|
| 55 | "grammar|g=s" => \$gramfile, |
|---|
| 56 | "outputfile|o=s" => \$outputfile); |
|---|
| 57 | |
|---|
| 58 | my $homedir = $ENV{'HOME'}; |
|---|
| 59 | $catfile =~ s/~/$homedir/; |
|---|
| 60 | $dicfile =~ s/~/$homedir/; |
|---|
| 61 | $gramfile =~ s/~/$homedir/; |
|---|
| 62 | $outputfile =~ s/~/$homedir/; |
|---|
| 63 | |
|---|
| 64 | |
|---|
| 65 | if($help) |
|---|
| 66 | { |
|---|
| 67 | print <<'END' |
|---|
| 68 | Usage: dgc [OPTIONS] |
|---|
| 69 | |
|---|
| 70 | Options: |
|---|
| 71 | --categories -c filename List of syntactic categories. |
|---|
| 72 | --dictionary -d filename Dictionary. |
|---|
| 73 | --grammar -g filename List of grammar rules. |
|---|
| 74 | --outputfile -o filename Output file name. |
|---|
| 75 | --help -h Help. |
|---|
| 76 | END |
|---|
| 77 | ; |
|---|
| 78 | exit 0; |
|---|
| 79 | } |
|---|
| 80 | |
|---|
| 81 | die("At least one of --cats and --dic must be given.\n") if !$catfile && !$dicfile; |
|---|
| 82 | |
|---|
| 83 | |
|---|
| 84 | our %in; #gramatyka wej¶ciowa |
|---|
| 85 | our %idx; #indeks gramatyki wej¶ciowej (niektóre stwierdzenia) |
|---|
| 86 | our %out; #gramatyka wyj¶ciowa |
|---|
| 87 | our %class; #tablica klas |
|---|
| 88 | |
|---|
| 89 | our $attr_re = $attr::attr_re; |
|---|
| 90 | our $cat_re = $attr::cat_re; |
|---|
| 91 | our $cats_re = qr/(?:$attr::cat_re\s*(?:,\s*$attr::cat_re)*)/; |
|---|
| 92 | our $class_re = qr/(?:\@\w+)/; |
|---|
| 93 | our $av_re = $attr::av_re; |
|---|
| 94 | our $avlist_re = $attr::avlist_re; |
|---|
| 95 | our $role_re = qr/(?:[[:lower:][:digit:]_]+)/; |
|---|
| 96 | our $prop_re = qr/(?:[[:upper:]]+)/; |
|---|
| 97 | our $proplist_re = qr/(?:(?:\&$prop_re)+)/; |
|---|
| 98 | |
|---|
| 99 | my $inputlineno=0; |
|---|
| 100 | |
|---|
| 101 | our $statementgrammar = q( |
|---|
| 102 | |
|---|
| 103 | statement : statement1 ";" { $item[1] } |
|---|
| 104 | |
|---|
| 105 | statement1: /cat/i acat { ['cat', { cat=>$item{acat}, catexp=>attr::parse($item{acat}) }, $item{acat}] } |
|---|
| 106 | | /flag/i flag { ['flag', { flag=>$item{flag} }, $item{flag}] } |
|---|
| 107 | | /role/i role { ['role', { role=>$item{role} }, $item{role}] } |
|---|
| 108 | | /left/i role { ['left', { role=>$item{role} }, 0] } |
|---|
| 109 | | /right/i role { ['right', { role=>$item{role} }, 0] } |
|---|
| 110 | | /sgl/i role { ['sgl', { role=>$item{role} }, 0] } |
|---|
| 111 | | /req/i xcat role { ['req', { cats=>$item{xcat}, role=>$item{role} }, 0] } |
|---|
| 112 | | /agr/i role attr { ['agr', { role=>$item{role}, attr=>$item{attr} }, $item{role}] } |
|---|
| 113 | | /gov/i role xcat { ['gov', { role=>$item{role}, cats=>$item{xcat} }, $item{role}] } |
|---|
| 114 | | /init/i flagconstr { ['initf', { flag=>$item{flagconstr} }, 0] } |
|---|
| 115 | | /fin/i flagconstr { ['finf', { flag=>$item{flagconstr} }, 0] } |
|---|
| 116 | | /init/i role { ['initr', { role=>$item{role} }, 0] } |
|---|
| 117 | | /fin/i role { ['finr', { role=>$item{role} }, 0] } |
|---|
| 118 | | /set/i xcat flag { ['set', { cats=>$item{xcat}, flag=>$item{flag} }, 0] } |
|---|
| 119 | | /pass/i role flag { ['pass', { role=>$item{role}, flag=>$item{flag} }, 0] } |
|---|
| 120 | | /constre/i role role { ['constre', { role1=>$item[2], role2=>$item[3] }, 0] } |
|---|
| 121 | | /constri/i role role { ['constri', { role1=>$item[2], role2=>$item[3] }, 0] } |
|---|
| 122 | |
|---|
| 123 | | /link/i xcat optflags(?) xcat optflags(?) role prop(s?) |
|---|
| 124 | { ['link', { hcats=>$item[2], hflagconstr=>$item[3], |
|---|
| 125 | dcats=>$item[4], dflagconstr=>$item[5], |
|---|
| 126 | role=>$item[6], props=>$item[7] }, 0] } |
|---|
| 127 | |
|---|
| 128 | | /long/i role role(s? /,/) '^' role(s? /,/) |
|---|
| 129 | { ['long', { rel=>$item[2], up=>$item[3], down=>$item[5] }, 0] } |
|---|
| 130 | |
|---|
| 131 | | /class/i classname '=' xcat { ['class', { name=>$item{classname}, cats=>$item{xcat} }, $item{classname}] } |
|---|
| 132 | |
|---|
| 133 | acat: /$attr::cat_re/ |
|---|
| 134 | |
|---|
| 135 | attr: /$attr::attr_re/ |
|---|
| 136 | |
|---|
| 137 | xcat: classexpr |
|---|
| 138 | |
|---|
| 139 | role: /\w+/ |
|---|
| 140 | |
|---|
| 141 | flag: /\w+/ |
|---|
| 142 | |
|---|
| 143 | optflags: "//" flagconstr { $item[2] } |
|---|
| 144 | |
|---|
| 145 | flagconstr: /\w+[+-]/ |
|---|
| 146 | |
|---|
| 147 | prop: '&' /\w+/ { $item[2] } |
|---|
| 148 | |
|---|
| 149 | classname: /\$\w+[+-]/ |
|---|
| 150 | |
|---|
| 151 | classexpr : classexpr1 '|' classexpr { main::union($item[1],$item[3]) } |
|---|
| 152 | | classexpr1 '~' classexpr { main::intersection( $item[1], main::complement($item[3]) ) } |
|---|
| 153 | | classexpr1 |
|---|
| 154 | |
|---|
| 155 | classexpr1 : classexpr2 '&' classexpr1 { main::intersection($item[1],$item[3]) } |
|---|
| 156 | | classexpr2 |
|---|
| 157 | |
|---|
| 158 | classexpr2 : '~' classexpr2 { main::complement($item[2]) } |
|---|
| 159 | | classexpr3 |
|---|
| 160 | |
|---|
| 161 | classexpr3 : classexpr4 '/' /$attr::avlist_re/ { main::intersection($item[1], [main::extension('*/' . $item[3])] ) } |
|---|
| 162 | | classexpr4 |
|---|
| 163 | |
|---|
| 164 | classexpr4 : class |
|---|
| 165 | | cat |
|---|
| 166 | | '(' classexpr ')' { $item[2] } |
|---|
| 167 | |
|---|
| 168 | class : classname { $main::class{$item[1]} or @{[]} } |
|---|
| 169 | |
|---|
| 170 | cat : /$main::cat_re/ { [main::extension($item[1])] } |
|---|
| 171 | |
|---|
| 172 | ); |
|---|
| 173 | |
|---|
| 174 | our $statementparser = Parse::RecDescent->new($statementgrammar); |
|---|
| 175 | |
|---|
| 176 | sub register |
|---|
| 177 | { |
|---|
| 178 | my ($src, $statement, $data, $index) = @_ ; |
|---|
| 179 | $data->{line} = $inputlineno; |
|---|
| 180 | $data->{src} = $src; |
|---|
| 181 | push @{$in{$statement}}, $data; |
|---|
| 182 | push @{$idx{$statement}{$index}}, $data if($index); |
|---|
| 183 | |
|---|
| 184 | if ($statement eq 'class') { $class{ $data->{name} } = $data->{cats} } |
|---|
| 185 | } |
|---|
| 186 | |
|---|
| 187 | |
|---|
| 188 | if(!$outputfile) { *OUTPUT = *STDOUT; } |
|---|
| 189 | elsif($outputfile eq "-") { *OUTPUT = *STDOUT; } |
|---|
| 190 | else { open(OUTPUT, ">$outputfile") or die("Can't open output file: $outputfile!"); } |
|---|
| 191 | |
|---|
| 192 | if(!$gramfile) { *INPUT = *STDIN; } |
|---|
| 193 | elsif($gramfile eq "-") { *INPUT = *STDIN; } |
|---|
| 194 | else { open(INPUT, "cat $gramfile | m4 |") or die("Unable to open: $gramfile!"); } |
|---|
| 195 | |
|---|
| 196 | # *INPUT = *STDIN; ############### TYMCZASOWO |
|---|
| 197 | |
|---|
| 198 | loadcats($catfile) if $catfile; |
|---|
| 199 | extractcats($dicfile) if $dicfile; |
|---|
| 200 | |
|---|
| 201 | |
|---|
| 202 | # CZYTANIE GRAMATYKI DGC |
|---|
| 203 | |
|---|
| 204 | while(<INPUT>) |
|---|
| 205 | { |
|---|
| 206 | $inputlineno++; |
|---|
| 207 | s/#.*//; |
|---|
| 208 | s/^\s+//; |
|---|
| 209 | s/\s+$//; |
|---|
| 210 | s/\s+/ /g; |
|---|
| 211 | next unless $_; |
|---|
| 212 | my $result = $statementparser->statement("$_;"); |
|---|
| 213 | |
|---|
| 214 | # print "#input line $inputlineno\n"; |
|---|
| 215 | # print Dumper($result); |
|---|
| 216 | |
|---|
| 217 | if($result) { register($_, @{$result}) } else { print STDERR "ERROR at line $inputlineno\n" } |
|---|
| 218 | } |
|---|
| 219 | |
|---|
| 220 | |
|---|
| 221 | # GENEROWANIE GRAMATYKI DGP |
|---|
| 222 | |
|---|
| 223 | my $inline = 0; |
|---|
| 224 | my $outline = 0; |
|---|
| 225 | |
|---|
| 226 | |
|---|
| 227 | # print Dumper($idx{gov}->{subj}); |
|---|
| 228 | |
|---|
| 229 | |
|---|
| 230 | for my $x (@{$in{cat}}) { print_outin("CAT $x->{cat}", $x); } |
|---|
| 231 | |
|---|
| 232 | for my $x (@{$in{flag}}) { print_outin("FLAG $x->{flag}", $x); } |
|---|
| 233 | |
|---|
| 234 | for my $x (@{$in{role}}) { print_outin("ROLE $x->{role}", $x); } |
|---|
| 235 | |
|---|
| 236 | for my $x (@{$in{long}}) { print_outin("LONG $x->{rel} " . join(",",@{$x->{up}}) . "^" . join(",",@{$x->{down}}), $x) } |
|---|
| 237 | |
|---|
| 238 | for my $x (@{$in{left}}) { print_outin("LEFT $x->{role}", $x) if chk_role($x->{role}, $x) } |
|---|
| 239 | |
|---|
| 240 | for my $x (@{$in{right}}) { print_outin("RIGHT $x->{role}", $x) if chk_role($x->{role}, $x) } |
|---|
| 241 | |
|---|
| 242 | for my $x (@{$in{sgl}}) { print_outin("SGL $x->{role}", $x) if chk_role($x->{role}, $x) } |
|---|
| 243 | |
|---|
| 244 | for my $x (@{$in{req}}) |
|---|
| 245 | { |
|---|
| 246 | if( chk_role($x->{role}, $x) ) |
|---|
| 247 | { |
|---|
| 248 | for my $atomcat (map{$_->{cat}} @{$x->{cats}}) |
|---|
| 249 | { |
|---|
| 250 | print_outin("REQ $atomcat $x->{role}", $x); |
|---|
| 251 | } |
|---|
| 252 | } |
|---|
| 253 | } |
|---|
| 254 | |
|---|
| 255 | for my $x (@{$in{initr}}) { print_outin("INITR $x->{role}", $x) if chk_role($x->{role}, $x) } |
|---|
| 256 | |
|---|
| 257 | for my $x (@{$in{finr}}) { print_outin("FINR $x->{role}", $x) if chk_role($x->{role}, $x) } |
|---|
| 258 | |
|---|
| 259 | for my $x (@{$in{initf}}) { print_outin("INITF $x->{flag}", $x) } # SPRAWDZIÆ CZY FLAGA JEST ZADEKLAROWANA |
|---|
| 260 | |
|---|
| 261 | for my $x (@{$in{finf}}) { print_outin("FINF $x->{flag}", $x); } # SPRAWDZIÆ CZY FLAGA JEST ZADEKLAROWANA |
|---|
| 262 | |
|---|
| 263 | for my $x (@{$in{set}}) |
|---|
| 264 | { |
|---|
| 265 | for my $atomcat (map{$_->{cat}} @{$x->{cats}}) |
|---|
| 266 | { |
|---|
| 267 | print_outin("SET $atomcat $x->{flag}", $x); |
|---|
| 268 | } |
|---|
| 269 | } |
|---|
| 270 | |
|---|
| 271 | for my $x (@{$in{pass}}) { print_outin("PASS $x->{role} $x->{flag}", $x); } |
|---|
| 272 | |
|---|
| 273 | for my $x (@{$in{constre}}) { print_outin("CONSTRE $x->{role1} $x->{role2}", $x) if chk_role($x->{role1}, $x) & chk_role($x->{role2}, $x) } |
|---|
| 274 | |
|---|
| 275 | for my $x (@{$in{constri}}) { print_outin("CONSTRI $x->{role1} $x->{role2}", $x) if chk_role($x->{role1}, $x) & chk_role($x->{role2}, $x) } |
|---|
| 276 | |
|---|
| 277 | for my $x (@{$in{link}}) |
|---|
| 278 | { |
|---|
| 279 | my @agrs = @{ $idx{agr}->{$x->{role} } or [] }; |
|---|
| 280 | my @govs = @{ $idx{gov}->{$x->{role} } or [] }; |
|---|
| 281 | |
|---|
| 282 | my @deps = (@govs > 0) ? @{ intersection( $x->{dcats}, map { $_->{cats} } @govs ) } : @{ $x->{dcats} } ; |
|---|
| 283 | |
|---|
| 284 | for my $head ( @{ $x->{hcats} } ) |
|---|
| 285 | { |
|---|
| 286 | DEP: |
|---|
| 287 | for my $dep (@deps) |
|---|
| 288 | { |
|---|
| 289 | for my $agr (@agrs) |
|---|
| 290 | { |
|---|
| 291 | next DEP unless attr::agree(@{$head->{catexp}},@{$dep->{catexp}},$agr->{attr}); |
|---|
| 292 | } |
|---|
| 293 | my $hflagconstr = @{$x->{hflagconstr}} ? "//@{$x->{hflagconstr}}" : ""; |
|---|
| 294 | my $dflagconstr = @{$x->{dflagconstr}} ? "//@{$x->{dflagconstr}}" : ""; |
|---|
| 295 | my $props = join('',map("\&$_", @{$x->{props}})); |
|---|
| 296 | |
|---|
| 297 | print_outin("LINK $head->{cat}$hflagconstr $dep->{cat}$dflagconstr $x->{role}$props",$x, @agrs, @govs); |
|---|
| 298 | } |
|---|
| 299 | } |
|---|
| 300 | } |
|---|
| 301 | |
|---|
| 302 | |
|---|
| 303 | sub chk_role |
|---|
| 304 | { |
|---|
| 305 | ($role, $statement_details) = @_; |
|---|
| 306 | if($idx{role}{$role}) { 1; } else { print_error("undefined role", $statement_details); 0; } |
|---|
| 307 | } |
|---|
| 308 | |
|---|
| 309 | sub print_outin |
|---|
| 310 | { |
|---|
| 311 | my ($out,@in) = (shift, @_); |
|---|
| 312 | print OUTPUT "$out\t\t#"; |
|---|
| 313 | printf OUTPUT " %04d@\"%s\"", $_->{line}, $_->{src} foreach @in; |
|---|
| 314 | print OUTPUT "\n"; |
|---|
| 315 | } |
|---|
| 316 | |
|---|
| 317 | sub print_error |
|---|
| 318 | { |
|---|
| 319 | my ($message,@in) = (shift,@_); |
|---|
| 320 | print STDERR "ERROR: $message in statement "; |
|---|
| 321 | printf STDERR " %04d@\"%s\"", $_->{line}, $_->{src} foreach @in; |
|---|
| 322 | print STDERR "\n"; |
|---|
| 323 | } |
|---|
| 324 | |
|---|
| 325 | |
|---|
| 326 | sub extractcats |
|---|
| 327 | { |
|---|
| 328 | my $file = shift; |
|---|
| 329 | open DICFILE, "$file"; |
|---|
| 330 | while(<DICFILE>) |
|---|
| 331 | { |
|---|
| 332 | while(/,([^[:space:];]+)/g) |
|---|
| 333 | { |
|---|
| 334 | my $cat=$1; |
|---|
| 335 | next if !$cat; # || exists $cats{$cat}; |
|---|
| 336 | # print OUTPUT "CAT $1\n"; |
|---|
| 337 | register('cat', {src=>"CAT $cat", cat=>"$cat", catexp=>attr::parse($cat)}, $cat); |
|---|
| 338 | } |
|---|
| 339 | } |
|---|
| 340 | close DICFILE; |
|---|
| 341 | } |
|---|
| 342 | |
|---|
| 343 | |
|---|
| 344 | sub loadcats |
|---|
| 345 | { |
|---|
| 346 | my $file = shift; |
|---|
| 347 | open CATFILE, "$file"; |
|---|
| 348 | while(<CATFILE>) |
|---|
| 349 | { |
|---|
| 350 | tr/ \t\n//d; |
|---|
| 351 | next if !$_; # || exists $cats{$_}; |
|---|
| 352 | # print OUTPUT "CAT $_\n"; |
|---|
| 353 | register("CAT $_", 'cat', {cat=>"$_", catexp=>attr::parse($_)}, $_); |
|---|
| 354 | } |
|---|
| 355 | close CATFILE; |
|---|
| 356 | } |
|---|
| 357 | |
|---|
| 358 | sub extension |
|---|
| 359 | { |
|---|
| 360 | my $cat = shift; |
|---|
| 361 | my $catexp = attr::parse($cat); |
|---|
| 362 | grep { attr::match(@{$_->{catexp}},@{$catexp}) } @{$in{cat}}; |
|---|
| 363 | } |
|---|
| 364 | |
|---|
| 365 | sub uniq { my %seen; grep { ! $seen{$_}++ } @_ } |
|---|
| 366 | sub union { [ uniq( map { @{$_} } @_ ) ] } |
|---|
| 367 | sub intersection { my $n=@_; my %seen; [ grep { ++$seen{$_} == $n } map { @{$_} } @_ ] } |
|---|
| 368 | sub complement { my %exclude; for $c (@{shift()}) { $exclude{$c}++ }; [ grep { ! $exclude{$_} } @{$in{cat}} ] } |
|---|
| 369 | |
|---|