comparison interps/clc-intercal/CLC-INTERCAL-Base-1.-94.-2/INTERCAL/Sick.pm @ 996:859f9b4339e6

<Gregor> tar xf egobot.tar.xz
author HackBot
date Sun, 09 Dec 2012 19:30:08 +0000
parents
children
comparison
equal deleted inserted replaced
995:6883f5911eb7 996:859f9b4339e6
1 package Language::INTERCAL::Sick;
2
3 # Compiler/user interface/whatnot for CLC-INTERCAL
4
5 # This file is part of CLC-INTERCAL
6
7 # Copyright (c) 2006-2008 Claudio Calvelli, all rights reserved.
8
9 # CLC-INTERCAL is copyrighted software. However, permission to use, modify,
10 # and distribute it is granted provided that the conditions set out in the
11 # licence agreement are met. See files README and COPYING in the distribution.
12
13 use strict;
14 use vars qw($VERSION $PERVERSION);
15 ($VERSION) = ($PERVERSION = "CLC-INTERCAL/Base INTERCAL/Sick.pm 1.-94.-2") =~ /\s(\S+)$/;
16
17 use Carp;
18 use File::Basename;
19 use File::Spec::Functions;
20 use Language::INTERCAL::Exporter '1.-94.-2';
21 use Language::INTERCAL::Charset '1.-94.-2', qw(charset_name toascii charset);
22 use Language::INTERCAL::GenericIO '1.-94.-2';
23 use Language::INTERCAL::Backend '1.-94.-2', qw(backend generate_code);
24 use Language::INTERCAL::Interpreter '1.-94.-2';
25
26 sub new {
27 @_ == 2 or croak "Usage: new Language::INTERCAL::Sick(RC)";
28 my ($class, $rc) = @_;
29 bless {
30 object_option => {
31 backend => '',
32 bug => 1,
33 charset => '',
34 name => '%o',
35 optimise => 0,
36 output => '%p.%s',
37 preload => [],
38 suffix => '',
39 trace => undef,
40 trace_fh => undef,
41 ubug => 0.01,
42 verbose => 0,
43 },
44 shared_option => {
45 default_backend => 'Object',
46 default_charset => [],
47 default_extra => [],
48 default_suffix => [],
49 preload_callback => undef,
50 },
51 sources => [],
52 filepath => {},
53 shared_filepath => {},
54 int_cache => {},
55 loaded => 0,
56 rc => $rc,
57 theft_server => 0,
58 server => 0,
59 }, $class;
60 }
61
62 sub reset {
63 @_ == 1 or croak "Usage: SICK->reset";
64 my ($sick) = @_;
65 $sick->{loaded} = 0;
66 $sick->{sources} = [];
67 $sick;
68 }
69
70 my %checkoption = (
71 backend => \&_load_backend,
72 bug => \&_check_bug,
73 charset => \&_load_charset,
74 default_backend => \&_load_backend,
75 default_charset => \&_load_charset,
76 default_extra => \&_check_extra,
77 default_suffix => \&_check_suffix,
78 optimise => \&_check_bool,
79 preload => \&_check_object,
80 preload_callback => \&_check_callback,
81 trace => \&_check_bool,
82 trace_fh => \&_check_filehandle,
83 ubug => \&_check_bug,
84 verbose => \&_check_filehandle,
85 );
86
87 my %object_type = (
88 IACC => 'COMPILER',
89 COMPILER => 'COMPILER',
90 ASSEMBLER => 'COMPILER',
91 RUNCOMPILER => 'COMPILER',
92 BASE => 'ONEONLY',
93 POSTPRE => 'ONEONLY',
94 EXTENSION => 'REPEAT',
95 OPTION => 'REPEAT',
96 OPTIMISER => 'REPEAT',
97 );
98
99 sub option {
100 @_ == 2 or @_ == 3 or croak "Usage: SICK->option(NAME [, VALUE])";
101 @_ == 2 ? shift->getoption(@_) : shift->setoption(@_);
102 }
103
104 sub getoption {
105 @_ == 2 or croak "Usage: SICK->getoption(NAME)";
106 my ($sick, $name) = @_;
107 my $value = exists $sick->{object_option}{$name}
108 ? $sick->{object_option}{$name}
109 : exists $sick->{shared_option}{$name}
110 ? $sick->{shared_option}{$name}
111 : die "Unknown option $name\n";
112 return $value unless ref $value;
113 return $value if UNIVERSAL::isa($value, 'Language::INTERCAL::GenericIO');
114 return @$value if 'ARRAY' eq ref $value;
115 return map { ($_ => [@{$value->{$_}}]) } keys %$value
116 if 'HASH' eq ref $value;
117 return (); # should never get here
118 }
119
120 sub setoption {
121 @_ == 3 or croak "Usage: SICK->setoption(NAME, VALUE)";
122 my ($sick, $name, $value) = @_;
123 my $hash = exists $sick->{object_option}{$name}
124 ? $sick->{object_option}
125 : exists $sick->{shared_option}{$name}
126 ? $sick->{shared_option}
127 : die "Unknown option $name\n";
128 if (exists $checkoption{$name}) {
129 $value = $checkoption{$name}->($name, $sick, $value);
130 }
131 if (! ref $hash->{$name}) {
132 $hash->{$name} = $value;
133 } elsif (UNIVERSAL::isa($value, 'Language::INTERCAL::GenericIO')) {
134 $hash->{$name} = $value;
135 } elsif ('ARRAY' eq ref $hash->{$name}) {
136 push @{$hash->{$name}}, $value;
137 } elsif ('HASH' eq ref $hash->{$name}) {
138 my ($key, $as, @add) = @$value;
139 if (exists $hash->{$name}{$key}) {
140 $hash->{$name}{$key}[0] = $as;
141 } else {
142 $hash->{$name}{$key} = [$as];
143 }
144 push @{$hash->{$name}{$key}}, @add;
145 } else {
146 # not supposed to get here
147 die "Cannot set option $name\n";
148 }
149 $sick;
150 }
151
152 sub clearoption {
153 @_ == 2 or croak "Usage: SICK->clearoption(NAME)";
154 my ($sick, $name) = @_;
155 my $hash = exists $sick->{object_option}{$name}
156 ? $sick->{object_option}
157 : exists $sick->{shared_option}{$name}
158 ? $sick->{shared_option}
159 : die "Unknown option $name\n";
160 if (ref $hash->{$name}) {
161 if (UNIVERSAL::isa($hash->{$name}, 'Language::INTERCAL::GenericIO')) {
162 $hash->{$name} = 0;
163 } elsif ('ARRAY' eq ref $hash->{$name}) {
164 $hash->{$name} = [];
165 } elsif ('HASH' eq ref $hash->{$name}) {
166 $hash->{$name} = {};
167 } else {
168 die "Cannot clear option $name\n";
169 }
170 } else {
171 die "Cannot clear option $name\n";
172 }
173 $sick;
174 }
175
176 sub alloptions {
177 @_ == 1 or @_ == 2 or croak "Usage: SICK->alloptions [(shared)]";
178 my ($sick, $shared) = @_;
179 my %vals = ();
180 my @hash = ();
181 push @hash, 'object_option' if ! defined $shared || ! $shared;
182 push @hash, 'shared_option' if ! defined $shared || ! $shared;
183 for my $hash (@hash) {
184 while (my ($name, $value) = each %{$sick->{$hash}}) {
185 if (! ref $value) {
186 # nothing, but we don't want to be caught in next cases
187 } elsif (UNIVERSAL::isa($value, 'Language::INTERCAL::GenericIO')) {
188 # nothing, but we don't want to be caught in next cases
189 } elsif ('ARRAY' eq ref $value) {
190 # a shallow copy will do -- we know values are strings
191 $value = [ @$value ];
192 } elsif ('HASH' eq ref $value) {
193 # two level deep copy: the values are arrays of strings
194 my %v = ();
195 while (my ($key, $val) = each %$value) {
196 $v{$key} = [ @$val ];
197 }
198 $value = \%v;
199 } elsif (ref $value) {
200 # WTF?
201 $value = undef;
202 }
203 $vals{$name} = $value;
204 }
205 }
206 %vals;
207 }
208
209 sub source {
210 @_ == 2 or croak "Usage: SICK->source(FILENAME)";
211 my ($sick, $file) = @_;
212 $file = _check_file($sick, $file);
213 push @{$sick->{sources}}, {
214 'source' => $file,
215 'option' => { $sick->alloptions(0) }, # don't copy shared options
216 'filepath' => $sick->{filepath},
217 };
218 $sick->{loaded} = 0;
219 $sick;
220 }
221
222 sub load_objects {
223 @_ == 1 or croak "Usage: SICK->load_objects()";
224 my ($sick) = @_;
225 return $sick if $sick->{loaded};
226 for (my $i = 0; $i < @{$sick->{sources}}; $i++) {
227 my $object = $sick->{sources}[$i];
228 next if exists $object->{object};
229 my $o = $object->{option};
230 my ($obj, $fn, $base, $is_src) = _load_source($sick, $object, $o);
231 $object->{is_src} = $is_src;
232 $object->{base} = $base;
233 $object->{object} = $obj;
234 $object->{filename} = $fn;
235 }
236 $sick->{loaded} = 1;
237 $sick;
238 }
239
240 sub save_objects {
241 @_ == 2 or croak "Usage: SICK->save_objects(AND_KEEP?)";
242 my ($sick, $keep) = @_;
243 $sick->load_objects();
244 for my $object (@{$sick->{sources}}) {
245 my $o = $object->{option};
246 my $backend = $o->{backend};
247 next unless $object->{is_src} || $backend ne 'Object';
248 my $out = $o->{output};
249 next if $out eq '';
250 $backend = $sick->{shared_option}{default_backend}
251 if $backend eq '';
252 my $v = $o->{verbose} ? sub {
253 my ($name) = @_;
254 $o->{verbose}->read_text($name eq '' ? 'Running...'
255 : "Saving $name... ");
256 } : '';
257 my $orig = $object->{source};
258 $orig =~ s/\.[^.]*$//;
259 my %op = (
260 verbose => $v,
261 );
262 generate_code($object->{object}, $backend, $o->{name},
263 $object->{base}, $out, $orig, \%op);
264 $o->{verbose}->read_text("OK\n") if $o->{verbose};
265 undef $object unless $keep;
266 }
267 $sick;
268 }
269
270 sub theft_server {
271 @_ == 2 or croak "Usage: SICK->theft_server(SERVER)";
272 my ($sick, $server) = @_;
273 $sick->{theft_server} = $server;
274 $sick;
275 }
276
277 sub server {
278 @_ == 2 or croak "Usage: SICK->server(SERVER)";
279 my ($sick, $server) = @_;
280 $sick->{server} = $server;
281 $sick;
282 }
283
284 sub get_object {
285 @_ == 2 or croak "Usage: SICK->get_object(NAME)";
286 my ($sick, $name) = @_;
287 for my $o (@{$sick->{sources}}) {
288 next if $o->{source} ne $name;
289 return $o->{object};
290 }
291 undef;
292 }
293
294
295 sub all_objects {
296 @_ == 2 || @_ == 3
297 or croak "Usage: SICK->all_objects(CALLBACK [, JUST_FLAGS])";
298 my ($sick, $callback, $just_flags) = @_;
299 for my $search (@{$sick->{rc}->getoption('include')}) {
300 opendir(SEARCH, $search) or next;
301 while (defined (my $ent = readdir SEARCH)) {
302 $ent =~ /^(.*)\.io$/ or next;
303 my $name = $1;
304 my $file = catfile($search, $ent);
305 -f $file or next;
306 eval {
307 my $fh = Language::INTERCAL::GenericIO->new('FILE', 'w', $file);
308 my $ob = Language::INTERCAL::Object->write($fh, $just_flags);
309 my $type = undef;
310 $ob->has_flag('TYPE')
311 and $type = $ob->flag_value('TYPE');
312 $callback->($name, $file, $type, $ob);
313 };
314 }
315 closedir SEARCH;
316 }
317 $sick;
318 }
319
320 # private methods follow
321
322 sub _check_bool {
323 my ($name, $sick, $value) = @_;
324 return $value if $value =~ /^\d+$/;
325 return 1 if $value =~ /^t(?:rue)?$/i;
326 return 1 if $value =~ /^y(?:es)?$/i;
327 return 0 if $value =~ /^f(?:alse)?$/i;
328 return 0 if $value =~ /^n(?:o)?$/i;
329 die "Invalid value for $name\: '$value'\n";
330 }
331
332 sub _check_filehandle {
333 my ($name, $sick, $value) = @_;
334 return $value if ref $value &&
335 UNIVERSAL::isa($value, 'Language::INTERCAL::GenericIO');
336 return undef if $value =~ /^\d+$/ && $value == 0;
337 return undef if $value =~ /^n(?:one)?$/i;
338 die "Invalid filehandle value '$value'\n";
339 }
340
341 sub _check_path {
342 my ($name, $sick, $value) = @_;
343 return $value if -d $value;
344 die "Invalid path '$value'\n";
345 }
346
347 sub _check_bug {
348 my ($name, $sick, $value) = @_;
349 $value =~ /^(?:\d+(?:\.\d*)?|\.\d+)$/
350 or die "Value '$value' is not a positive number\n";
351 $value <= 100
352 or die "Value '$value' is too large for a probability\n";
353 $value;
354 }
355
356 sub _check_extra {
357 my ($name, $sick, $value) = @_;
358 ref $value && ref $value eq 'ARRAY'
359 or die "Invalid value for $name (must be a array ref)\n";
360 @$value == 3
361 or die "Invalid value for $name (requires three elements)\n";
362 my ($extra, $preload, $as) = @$value;
363 ref $preload && ref $preload eq 'ARRAY'
364 or die "Invalid value for $name (preloads must be array ref)\n";
365 [$extra, $preload, $as];
366 }
367
368 sub _check_suffix {
369 my ($name, $sick, $value) = @_;
370 ref $value && ref $value eq 'ARRAY'
371 or die "Invalid value for $name (must be a array ref)\n";
372 @$value == 3
373 or die "Invalid value for $name (requires three elements)\n";
374 my ($suffix, $as, $map) = @$value;
375 ref $map && ref $map eq 'HASH'
376 or die "Invalid value for $name (third element must be hash ref)\n";
377 exists $map->{''} && ref $map->{''} && ref $map->{''} eq 'ARRAY'
378 or die "Invalid value for $name (preloads must be array ref)\n";
379 # suffix map have alternatives expressed as something like
380 # ./2:3:4:5:6:7/i => .2i .3i ... .7i
381 # ./l:n:g:t://i => .li .ni .gi .ti .lni .nli ...
382 # note that we have no nesting of alternatives; use different rules
383 my @resplit = ();
384 my $regex = '';
385 while ($suffix =~ s#^(.*?)/##) {
386 $regex .= quotemeta($1);
387 $suffix =~ s#^(.*?)/##
388 or die "Invalid value for $name\: unclosed / in suffix\n";
389 my @extra = split(/:/, $1);
390 for my $extra (@extra) {
391 exists $map->{$extra} or next;
392 ref $map->{$extra} && ref $map->{$extra} eq 'ARRAY'
393 or die "Invalid value for $name " .
394 "(preloads for $extra must be array ref)\n";
395 }
396 my $extra = join('|', map { quotemeta } @extra);
397 my $star = $suffix =~ s#^:## ? '*' : '';
398 $regex .= '((?:' . $extra . ')' . $star . ')';
399 push @resplit, qr/^($extra)/;
400 }
401 $regex .= quotemeta($suffix) . '$';
402 $regex = qr/$regex/i;
403 return [$regex, $as, \@resplit, $map];
404 }
405
406 sub _find_file {
407 my ($sick, $value, $ftype, $cache, $path) = @_;
408 return $cache->{$value} if exists $cache->{$value};
409 # try opening file from current directory
410 if (-f $value) {
411 $cache->{$value} = $value;
412 return $value;
413 }
414 if (! file_name_is_absolute($value)) {
415 my ($file, $dir) = fileparse($value);
416 $path = $sick->{rc}->getoption('include') if ! defined $path;
417 for my $search (@$path) {
418 my $n = catfile($search, $dir, $file);
419 $n = canonpath($n);
420 if (-f $n) {
421 $cache->{$value} = $n;
422 return $n;
423 }
424 }
425 }
426 die "Cannot find $ftype \"$value\"\n";
427 }
428
429 sub _check_file {
430 my ($sick, $value) = @_;
431 _find_file($sick, $value, 'file',
432 $sick->{filecache},
433 $sick->{rc}->getoption('include'));
434 $value;
435 }
436
437 sub _find_object {
438 my ($sick, $value, $cache, $path) = @_;
439 if ($value !~ /\.ior?$/) {
440 # try adding suffix first
441 my $v = eval {
442 _find_file($sick, $value . '.io', 'object', $cache, $path);
443 };
444 unless ($@) {
445 return $v . 'r' if -f $v . 'r';
446 return $v;
447 }
448 }
449 _find_file($sick, $value, 'object', $cache, $path);
450 }
451
452 sub _check_object {
453 my ($name, $sick, $value) = @_;
454 # _find_object($sick, $value,
455 # $sick->{filecache},
456 # $sick->{rc}->getoption('include'));
457 $value;
458 }
459
460 sub _check_callback {
461 my ($name, $sick, $value) = @_;
462 ! $value and return $value; # unset callback
463 ref $value && UNIVERSAL::isa($value, 'CODE')
464 and return [$value];
465 ref $value && UNIVERSAL::isa($value, 'ARRAY')
466 or die "Invalid callback, must be a CODE or ARRAY reference\n";
467 ref $value->[0] && UNIVERSAL::isa($value->[0], 'CODE')
468 or die "Invalid callback, first element must be a CODE reference\n";
469 $value;
470 }
471
472 sub _open_file {
473 my ($sick, $source, $cache, $path) = @_;
474 my $fn = _find_file($sick, $source, 'file', $cache, $path);
475 my $fh = Language::INTERCAL::GenericIO->new('FILE', 'w', $fn);
476 ($fn, $fh);
477 }
478
479 sub _load_backend {
480 my ($name, $sick, $value) = @_;
481 defined backend($value)
482 or die "Invalid backend: $value";
483 $value;
484 }
485
486 sub _load_charset {
487 my ($name, $sick, $value) = @_;
488 defined charset_name($value)
489 or die "Invalid charset: $value\n";
490 $value;
491 }
492
493 sub _load_source {
494 my ($sick, $source, $o) = @_;
495 my ($fn, $fh) = _open_file($sick, $source->{source},
496 $source->{filepath},
497 $sick->{rc}->getoption('include'));
498 $o->{verbose}->read_text("$fn... ") if $o->{verbose};
499 my $base = $fn;
500 my $suffix = '';
501 if ($o->{suffix}) {
502 $suffix = $o->{suffix};
503 $suffix = '.' . $suffix if $suffix !~ /^\./;
504 $base =~ s/(\.[^.]*)$//; # remove and ignore suffix
505 } elsif ($base =~ s/(\.[^.]*)$//) {
506 $suffix = lc($1);
507 }
508 # first see if it is a real object (you never know)
509 my $int = eval {
510 Language::INTERCAL::Interpreter->write($sick->{rc}, $fh);
511 };
512 if (defined $int && ref $int) {
513 $o->{verbose}->read_text("[COMPILER OBJECT]\n") if $o->{verbose};
514 $int->server($sick->{server});
515 $int->theft_server($sick->{theft_server});
516 $int->setreg('TRFH', $o->{trace_fh}) if defined $o->{trace_fh};
517 $int->setreg('TM', $o->{trace}) if defined $o->{trace};
518 return ($int, $fn, $base, 0);
519 }
520 # failed for whatever reason, we'll try loading as a source
521 $fh->reset();
522 my @preload = @{$o->{preload}};
523 @preload = _guess_preloads($sick, $suffix, $o)
524 unless @preload;
525 # try to find a compiler
526 my @options = ();
527 my @compiler = ();
528 my %preloaded = ();
529 for my $p (@preload, 'postpre') {
530 next if $p eq '';
531 _preload($sick, $p, $source->{filepath}, $o, \%preloaded,
532 \@options, \@compiler);
533 }
534 exists $preloaded{COMPILER}
535 or die "Invalid preload list: no compiler\n";
536 # load the compiler and run it if required
537 if ($compiler[1]) {
538 # compiler saved using RunObject
539 $int = $compiler[0];
540 } else {
541 # compiler saved using Object - create a new interpreter and run the
542 # compiler in it
543 $int = Language::INTERCAL::Interpreter->new($sick->{rc});
544 unshift @options, $compiler[0];
545 }
546 $int->server($sick->{server});
547 $int->theft_server($sick->{theft_server});
548 $int->setreg('TRFH', $o->{trace_fh}) if defined $o->{trace_fh};
549 $int->setreg('TM', $o->{trace}) if defined $o->{trace};
550 my $obj = $int->object;
551 if ($o->{bug} > 0) {
552 $obj->setbug(0, $o->{bug});
553 } else {
554 $obj->setbug(1, $o->{ubug});
555 }
556 # execute all the options
557 for my $p (@options) {
558 $int->start(1)->run($p)->stop();
559 }
560 # do we need to guess character set?
561 my $chr = $o->{charset};
562 if ($chr eq '') {
563 $chr = _guess_charset($sick, $source->{source}, $fh);
564 }
565 $fh->write_charset($chr);
566 $fh->reset();
567 # now read file
568 my $line = 1;
569 my $col = 1;
570 my $scount = 0;
571 my $text = $fh->write_text('');
572 $o->{verbose}->read_text("\n source: " . length($text) . " bytes")
573 if $o->{verbose};
574 $obj->source($text);
575 $int->verbose_compile($o->{verbose});
576 $int->compile($text);
577 $o->{verbose}->read_text(" [object: " . _int_size($obj) . " bytes]")
578 if $o->{verbose};
579 $o->{verbose}->read_text("\n") if $o->{verbose};
580 return ($int, $fn, $base, 1);
581 }
582
583 sub _preload {
584 my ($sick, $file, $cache, $o, $preloaded, $options, $compiler) = @_;
585 my $fn = _find_object($sick, $file, $cache,
586 $sick->{rc}->getoption('include'));
587 $o->{verbose}->read_text("\n [$file: $fn") if $o->{verbose};
588 my ($ci, $size);
589 if (exists $sick->{int_cache}{$fn}) {
590 ($ci, $size) = @{$sick->{int_cache}{$fn}};
591 if ($o->{verbose} && ! $size) {
592 $sick->{int_cache}{$fn}[1] = $size = _int_size($ci);
593 }
594 } else {
595 my $fh = Language::INTERCAL::GenericIO->new('FILE', 'w', $fn);
596 $ci = Language::INTERCAL::Interpreter->write($sick->{rc}, $fh);
597 $size = $o->{verbose} ? _int_size($ci) : 0;
598 $sick->{int_cache}{$fn} = [$ci, $size];
599 }
600 $ci->object->has_flag('TYPE')
601 or die "Invalid object - did not provide a type\n";
602 my $ct = $ci->object->flag_value('TYPE');
603 exists $object_type{$ct} or die "Invalid object type: $ct\n";
604 my $ot = $object_type{$ct};
605 if ($ot eq 'COMPILER') {
606 exists $preloaded->{$ct}
607 and die "Invalid preloads list - compiler " .
608 "$preloaded->{$ot} already loaded\n";
609 $preloaded->{$ot} = $file;
610 $compiler->[0] = $ci;
611 $compiler->[1] = $ct eq 'RUNCOMPILER';
612 } elsif ($ot eq 'ONEONLY') {
613 exists $preloaded->{$ct}
614 and die "Invalid preloads list - \L$ct\E " .
615 "$preloaded->{$ct} already loaded\n";
616 $preloaded->{$ct} = $file;
617 push @$options, $ci;
618 } elsif ($ot eq 'REPEAT') {
619 push @$options, $ci;
620 } else {
621 die "Internal error, unmapped type $ot\n";
622 }
623 # if they want to do additional checks, let them
624 if ($sick->{shared_option}{preload_callback}) {
625 my ($code, @args) = @{$sick->{shared_option}{preload_callback}};
626 $code->($sick, $file, $fn, $ct, @args);
627 }
628 $o->{verbose}->read_text(": type \L$ct\E: $size bytes]") if $o->{verbose};
629 }
630
631 sub _guess_extra {
632 my ($sick, $extra) = @_;
633 for my $xd (@{$sick->{shared_option}{default_extra}}) {
634 my ($x, $preload, $as) = @$xd;
635 next if $x ne $extra;
636 return ($preload, $as);
637 }
638 ();
639 }
640
641 sub _guess_preloads {
642 my ($sick, $suffix, $o) = @_;
643 # must guess preloads from suffix
644 for my $sd (@{$sick->{shared_option}{default_suffix}}) {
645 my ($regex, $as, $resplit, $map) = @$sd;
646 next unless ref $resplit;
647 my @extra = $suffix =~ $regex;
648 next unless @extra;
649 if (@$resplit) {
650 my @e = ();
651 for my $r (@$resplit) {
652 my $e = shift @extra;
653 next unless defined $e;
654 while ($e =~ s/$r//) {
655 push @e, $1;
656 }
657 die "Internal error in _guess_preloads\n" if $e ne '';
658 }
659 @extra = @e;
660 } else {
661 @extra = ();
662 }
663 my @preloads = ();
664 my %preloads = ();
665 for my $p (@{$map->{''}}) {
666 my $q = $p;
667 if ($q =~ s/^\?//) {
668 next unless $o->{optimise};
669 }
670 push @preloads, $q;
671 $preloads{$q} = 1;
672 }
673 my @as = ( $as );
674 my %as = ( $as => 1 );
675 for my $extra (@extra) {
676 my ($_p, $a);
677 if (exists $map->{$extra}) {
678 ($_p, $a) = @{$map->{$extra}};
679 } else {
680 ($_p, $a) = _guess_extra($sick, $extra);
681 die "Inconsistent sickrc: $extra?\n" unless defined $_p;
682 }
683 for my $p (@$_p) {
684 my $q = $p;
685 if ($q =~ s/^\?//) {
686 next unless $o->{optimise};
687 }
688 next if exists $preloads{$q};
689 push @preloads, $q;
690 $preloads{$q} = 1;
691 }
692 next if $a eq '' || exists $as{$a};
693 push @as, $a;
694 $as{$a} = 1;
695 }
696 $o->{verbose}->read_text(" [" . join(' + ', @as) . "]")
697 if $o->{verbose};
698 return @preloads;
699 }
700 die "Cannot guess file type\n";
701 }
702
703 sub _guess_charset {
704 my ($sick, $source, $fh) = @_;
705 my %counts = ();
706 for my $name (@{$sick->{shared_option}{default_charset}}) {
707 eval {
708 my $cnv = toascii($name);
709 my $count = 0;
710 while ((my $line = $fh->write_binary(4096)) ne '') {
711 my $cl = &$cnv($line);
712 $count++ while $line =~ /DO|PLEASE/ig;
713 }
714 $counts{$name} = $count;
715 };
716 $fh->reset();
717 }
718 my @counts =
719 sort {$counts{$b} <=> $counts{$a}} grep {$counts{$_}} keys %counts;
720 if (@counts == 0 && $fh->write_binary(1) eq '') {
721 $fh->reset();
722 @counts = qw(ASCII);
723 $counts{ASCII} = 1;
724 }
725 if (! @counts || $counts{$counts[0]} < 1) {
726 my $cr = $sick->{object_option}{verbose} ? "\n" : "";
727 die "${cr}File \"$source\": cannot guess character set\n";
728 }
729 $counts[0];
730 }
731
732 sub _int_size {
733 my ($int) = @_;
734 my $size = 0;
735 my $fh = new Language::INTERCAL::GenericIO 'COUNT', 'r', \$size;
736 $int->read($fh);
737 $size;
738 }
739
740 1