2 # This file is part of Language::Befunge.
3 # Copyright (c) 2001-2008 Jerome Quelin, all rights reserved.
5 # This program is free software; you can redistribute it and/or modify
6 # it under the same terms as Perl itself.
10 package Language
::Befunge
::Storage
::Generic
::Vec
;
14 no warnings
'portable'; # "Bit vector size > 32 non-portable" warnings on x64
16 use Language
::Befunge
::Vector
;
17 use Language
::Befunge
::IP
;
18 use base
qw{ Language
::Befunge
::Storage
};
21 my $cell_size_in_bytes = $Config{ivsize
};
22 my $cell_size_in_bits = $cell_size_in_bytes * 8;
26 # try to load speed-up LBSGVXS
27 eval 'use Language::Befunge::Storage::Generic::Vec::XS';
28 if ( defined $Language::Befunge
::Storage
::Generic
::Vec
::XS
::VERSION
) {
29 my $xsversion = $Language::Befunge
::Vector
::XS
::VERSION
;
31 get_value _get_value set_value _set_value _offset __offset _is_xs expand _expand
33 foreach my $sub ( @subs ) {
35 no warnings
'redefine';
36 my $lbsgvxs_sub = "Language::Befunge::Storage::Generic::Vec::XS::$sub";
37 *$sub = \
&$lbsgvxs_sub;
45 # Creates a new Lahey Space.
49 my $dimensions = shift;
51 my $usage = "Usage: $package->new(\$dimensions, Wrapping => \$wrapping)";
52 croak
$usage unless defined $dimensions;
53 croak
$usage unless $dimensions > 0;
54 croak
$usage unless exists $args{Wrapping
};
57 wrapping
=> $args{Wrapping
},
59 bless $self, $package;
74 $$self{min
} = Language
::Befunge
::Vector
->new_zeroes($$self{nd
});
75 $$self{max
} = Language
::Befunge
::Vector
->new_zeroes($$self{nd
});
76 $$self{torus
} = chr(0) x
$cell_size_in_bytes;
77 $self->set_value($$self{min
}, 32);
84 # Expand the torus to include the provided point.
87 my ($self, $point) = @_;
88 my ($old_min, $old_max) = ($$self{min
}, $$self{max
});
89 return if $point->bounds_check($$self{min
}, $$self{max
});
91 $point = $point->copy();
94 my ($new_min, $new_max) = ($old_min->copy, $old_max->copy);
95 foreach my $d (0..$nd-1) {
96 $new_min->set_component($d, $point->get_component($d))
97 if $new_min->get_component($d) > $point->get_component($d);
98 $new_max->set_component($d, $point->get_component($d))
99 if $new_max->get_component($d) < $point->get_component($d);
101 my $old_size = $old_max - $old_min;
102 my $new_size = $new_max - $new_min;
104 # if we have nothing to do, skip out early.
105 return if $old_size == $new_size;
107 # figure out the new storage size
108 my $storage_size = $self->_offset($new_max, $new_min, $new_max) + 1;
110 # figure out what a space looks like on this architecture.
111 # Note: vec() is always big-endian, but the XS module is host-endian.
112 # So we have to use an indirect approach.
113 my $old_value = $self->get_value($self->min);
114 $self->set_value($self->min, 32);
115 my $new_value = vec($$self{torus
}, 0, $cell_size_in_bits);
116 $self->set_value($self->min, $old_value);
117 # allocate new storage
118 my $new_torus = " " x
$cell_size_in_bytes;
119 vec($new_torus, 0, $cell_size_in_bits) = $new_value;
120 $new_torus x
= $storage_size;
121 for(my $v = $new_min->copy; defined($v); $v = $v->rasterize($new_min, $new_max)) {
122 if($v->bounds_check($old_min, $old_max)) {
123 my $length = $old_max->get_component(0) - $v->get_component(0);
124 my $old_offset = $self->_offset($v);
125 my $new_offset = $self->_offset($v, $new_min, $new_max);
126 vec( $new_torus , $new_offset, $cell_size_in_bits)
127 = vec($$self{torus
}, $old_offset, $cell_size_in_bits);
130 $$self{min
} = $new_min;
131 $$self{max
} = $new_max;
132 $$self{torus
} = $new_torus;
137 # store( code, [vector] )
139 # Store the given code at the specified vector. If the coordinates
140 # are omitted, then the code is stored at the origin (0, 0).
142 # Return the size of the code inserted, as a vector.
144 # The code is a string, representing a block of Funge code. Rows are
145 # separated by newlines. Planes are separated by form feeds. A complete list
146 # of separators follows:
154 # The new-line and form-feed delimiters are in the Funge98 spec. However,
155 # there is no standardized separator for dimensions above Z. Currently,
156 # dimensions 4 and above use \0, \0\0, \0\0\0, etc. These are dangerously
157 # ambiguous, but are the only way I can think of to retain reverse
158 # compatibility. Suggestions for better delimiters are welcome. (Using XML
159 # would be really ugly, I'd prefer not to.)
161 # This function actually enumerates the input twice: once to determine the max
162 # (so we only have to call expand() once), and once to do the actual storing.
165 my ($self, $code, $base) = @_;
167 $base = Language
::Befunge
::Vector
->new_zeroes($$self{nd
}) unless defined $base;
169 # support for any eol convention
170 $code =~ s/\r\n/\n/g;
173 # The torus is a tree of arrays of numbers.
174 # The tree is N levels deep, where N is the number of dimensions.
175 # Each number is the ordinal value of the character held in this cell.
177 my @separators = ("", "\n", "\f");
178 push(@separators, "\0"x
($_-3)) for (4..$nd); # , "\0", "\0\0", "\0\0\0"...
179 my @sizes = map { 0 } (1..$nd);
180 sub _code_split_helper
{
181 my ($d, $code, $sep, $sizes) = @_;
182 my $rv = [split($$sep[$d], $code)];
183 $rv = [ map { _code_split_helper
($d-1,$_,$sep,$sizes) } (@
$rv) ]
185 $$sizes[$d] = scalar @
$rv if scalar @
$rv > $$sizes[$d];
188 my $coderef = _code_split_helper
($nd - 1, $code, \
@separators, \
@sizes);
190 # Figure out the rectangle size and the end-coordinate (max).
191 my $size = Language
::Befunge
::Vector
->new(@sizes);
192 my $max = Language
::Befunge
::Vector
->new(map { $_ - 1 } (@sizes));
195 # Enlarge torus to make sure our new values will fit.
196 $self->expand( $base );
197 $self->expand( $max );
200 TOP
: for(my $v = $base->copy; defined($v); $v = $v->rasterize($base, $max)) {
203 foreach my $ent (reverse $cv->get_all_components()) {
204 next TOP
unless exists $$code[$ent];
205 $code = $$code[$ent];
207 next TOP
if $code eq ' ';
208 $self->set_value($v, ord($code));
216 # store_binary( code, [vector] )
218 # Store the given code at the specified coordinates. If the coordinates
219 # are omitted, then the code is stored at the Origin(0, 0) coordinates.
221 # Return the size of the code inserted, as a vector.
223 # This is binary insertion, that is, EOL and FF sequences are stored in
224 # Funge-space instead of causing the dimension counters to be reset and
225 # incremented. The data is stored all in one row.
228 my ($self, $code, $base) = @_;
230 $base = Language
::Befunge
::Vector
->new_zeroes($$self{nd
})
231 unless defined $base;
233 # The torus is a tree of arrays of numbers.
234 # The tree is N levels deep, where N is the number of dimensions.
235 # Each number is the ordinal value of the character held in this cell.
237 my @sizes = length($code);
238 push(@sizes,1) for(2..$nd);
240 # Figure out the min, max, and size
241 my $size = Language
::Befunge
::Vector
->new(@sizes);
242 my $max = Language
::Befunge
::Vector
->new(map { $_ - 1 } (@sizes));
245 # Enlarge torus to make sure our new values will fit.
246 $self->expand( $base );
247 $self->expand( $max );
250 for(my $v = $base->copy; defined($v); $v = $v->rasterize($base, $max)) {
251 my $char = substr($code, 0, 1, "");
252 next if $char eq " ";
253 $self->set_value($v, ord($char));
262 # Return the character stored in the torus at the specified location. If
263 # the value is not between 0 and 255 (inclusive), get_char will return a
264 # string that looks like "<np-0x4500>".
266 # B</!\> As in Funge, code and data share the same playfield, the
267 # character returned can be either an instruction B<or> raw data. No
268 # guarantee is made that the return value is printable.
273 my $ord = $self->get_value($v);
274 # reject invalid ascii
275 return sprintf("<np-0x%x>",$ord) if ($ord < 0 || $ord > 255);
281 # my $val = get_value( vector )
283 # Return the number stored in the torus at the specified location. If
284 # the value hasn't yet been set, it defaults to the ordinal value of a
287 # B</!\> As in Funge, code and data share the same playfield, the
288 # number returned can be either an instruction B<or> a data (or even
289 # both... Eh, that's Funge! :o) ).
295 if ($v->bounds_check($$self{min
}, $$self{max
})) {
296 my $off = $self->_offset($v);
297 $val = vec($$self{torus
}, $off, $cell_size_in_bits);
299 return $self->_u32_to_s32($val);
304 # set_value( vector, value )
306 # Write the supplied value in the torus at the specified location.
308 # B</!\> As in Funge, code and data share the same playfield, the
309 # number stored can be either an instruction B<or> a data (or even
310 # both... Eh, that's Funge! :o) ).
313 my ($self, $v, $val) = @_;
315 # Ensure we can set the value.
317 my $off = $self->_offset($v);
318 vec($$self{torus
}, $off, $cell_size_in_bits) = $self->_s32_to_u32($val);
323 # my $str = rectangle( start, size )
325 # Return a string containing the data/code in the specified rectangle.
327 # Note that for useful data to be returned, the "size" vector must contain at
328 # least a "1" for each component. After all, a world of 10 width, 10 length
329 # but zero height would contain 0 planes. So, if any components of the
330 # "size" vector do contain a 0, "" is returned.
333 my ($self, $v1, $v2) = @_;
339 foreach my $d (0..$nd-1) {
340 # each dimension must >= 1, otherwise the rectangle will be empty.
341 return "" unless $v2->get_component($d);
342 # ... but we need to offset by -1, to calculate $max
343 $v2->set_component($d, $v2->get_component($d) - 1);
346 # No separator is used for the first dimension, for obvious reasons.
347 # Funge98 specifies lf/cr/crlf for a second-dimension separator.
348 # Funge98 specifies a form feed for a third-dimension separator.
349 # Funge98 doesn't specify what dimensions 4 and above should use.
350 # We use increasingly long strings of null bytes.
351 # (4d uses 1 null byte, 5d uses 2, 6d uses 3, etc)
353 push(@separators,"\n") if $nd > 1;
354 push(@separators,"\f") if $nd > 2;
355 push(@separators,"\0"x
($_-3)) for (4..$nd); # , "\0", "\0\0", "\0\0\0"...
356 my $prev = $min->copy;
357 for(my $v = $min->copy; defined($v); $v = $v->rasterize($min, $max)) {
358 foreach my $d (0..$$self{nd
}-1) {
359 $data .= $separators[$d]
360 if $prev->get_component($d) != $v->get_component($d);
363 $data .= $self->get_char($v);
370 # my %labels = labels_lookup( )
372 # Parse the Lahey space to find sequences such as C<;:(\w[^\s;])[^;]*;>
373 # and return a hash reference whose keys are the labels and the values
374 # an anonymous array with two vectors: a vector describing the absolute
375 # position of the character B<just after> the trailing C<;>, and a
376 # vector describing the velocity that lead to this label.
378 # This method will only look in the cardinal directions; west, east,
379 # north, south, up, down and so forth.
381 # This allow to define some labels in the source code, to be used by
382 # C<Inline::Befunge> (and maybe some extensions).
388 my ($min, $max) = ($$self{min
}, $$self{max
});
392 foreach my $dimension (0..$nd-1) {
393 # for the loop below, $max actually needs to be the point *after* the
394 # greatest point ever written to; otherwise the last column is skipped.
395 $max->set_component($dimension, $max->get_component($dimension)+1);
397 # build the array of (non-diagonal) vectors
398 my $v1 = Language
::Befunge
::Vector
->new_zeroes($nd);
400 $v1->set_component($dimension,-1);
401 push(@directions,$v1);
402 $v2->set_component($dimension, 1);
403 push(@directions,$v2);
406 R
: for(my $this = $min->copy; defined($this); $this = $this->rasterize($min, $max)) {
407 V
: for my $v (@directions) {
408 next R
unless $self->get_char($this) eq ";";
409 my ($label, $loc) = $self->_labels_try( $this, $v );
410 next V
unless defined($label);
412 # How exciting, we found a label!
413 croak
"Help! I found two labels '$label' in the funge space"
414 if exists $labels->{$label};
415 $$labels{$label} = [$loc, $v];
426 # Returns a Vector object, pointing at the beginning of the torus.
427 # If nothing has been stored to a negative offset, this Vector will
428 # point at the origin (0,0).
432 return $$self{min
}->copy;
439 # Returns a Vector object, pointing at the end of the torus.
440 # This is usually the largest position which has been written to.
444 return $$self{max
}->copy;
451 # _offset(v [, min, max])
453 # Return the offset (within the torus bitstring) of the vector. If min and max
454 # are provided, return the offset within a hypothetical torus which has those
458 my ($self, $v, $min, $max) = @_;
460 my $off_by_1 = Language
::Befunge
::Vector
->new(map { 1 } (1..$nd));
461 $min = $$self{min
} unless defined $min;
462 $max = $$self{max
} unless defined $max;
463 my $tsize = $max + $off_by_1 - $min;
464 my $toff = $v - $min;
467 foreach my $d (0..$nd-1) {
468 $rv += $toff->get_component($d) * $levsize;
469 $levsize *= $tsize->get_component($d);
476 # _labels_try( $start, $delta )
478 # Try in the specified direction if the funge space matches a label
479 # definition. Return undef if it wasn't a label definition, or the name
480 # of the label if it was a valid label.
482 # called internally by labels_lookup().
485 my ($self, $start, $delta) = @_;
487 my $wrapping = $$self{wrapping
};
488 my $ip = Language
::Befunge
::IP
->new($$self{nd
});
489 my $min = $self->min;
490 my $max = $self->max;
491 $ip->set_position($start->copy);
492 $ip->set_delta($delta);
494 # don't affect the parent
495 #$vector = $vector->copy();
496 # Fetch the whole comment stuff.
498 # Calculate the next cell coordinates.
499 my $v = $ip->get_position;
500 my $d = $ip->get_delta;
502 # now, let's move the ip.
505 if ( $v->bounds_check($min, $max) ) {
506 $ip->set_position( $v );
508 $wrapping->wrap( $self, $ip );
511 $comment .= $self->get_char($ip->get_position());
512 } while ( $comment !~ /;.$/ );
514 # Check if the comment matches the pattern.
515 $comment =~ /^:(\w[^\s;]*)[^;]*;.$/;
516 return undef unless defined $1;
517 return undef unless length $1;
518 return ($1, $ip->get_position());
523 my ($self, $value) = @_;
524 $value = 0xffffffff + ($value+1)
530 my ($self, $value) = @_;
531 $value = -2147483648 + ($value & 0x7fffffff)
532 if($value & 0x80000000);
543 Language::Befunge::LaheySpace::Generic - a generic N-dimensional LaheySpace.
548 # create a 3-dimensional LaheySpace.
549 my $torus = Language::Befunge::LaheySpace::Generic->new(3);
551 $torus->store(<<"EOF");
558 Note you usually don't need to use this module directly.
559 B<Language::Befunge::Interpreter> can optionally use it. If you are
560 considering using it, you should really install
561 L<Language::Befunge::Storage::Generic::Vec::XS> too, as this module is
562 dreadfully slow without it. If you cannot install that, you should
563 use L<Language::Befunge::Storage::Generic::AoA> instead, it will perform
569 This module implements a traditional Lahey space.
574 =head2 new( dimensions )
576 Creates a new Lahey Space.
579 =head1 PUBLIC METHODS
588 Expand the torus to include the provided point.
591 =head2 store( code, [vector] )
593 Store the given code at the specified vector. If the coordinates
594 are omitted, then the code is stored at the origin (0, 0).
596 Return the size of the code inserted, as a vector.
598 The code is a string, representing a block of Funge code. Rows are
599 separated by newlines. Planes are separated by form feeds. A complete list of
608 The new-line and form-feed delimiters are in the Funge98 spec. However, there
609 is no standardized separator for dimensions above Z. Currently, dimensions 4
610 and above use \0, \0\0, \0\0\0, etc. These are dangerously ambiguous, but are
611 the only way I can think of to retain reverse compatibility. Suggestions for
612 better delimiters are welcome. (Using XML would be really ugly, I'd prefer not
616 =head2 store_binary( code, [vectir] )
618 Store the given code at the specified coordinates. If the coordinates
619 are omitted, then the code is stored at the Origin(0, 0) coordinates.
621 Return the size of the code inserted, as a vector.
623 This is binary insertion, that is, EOL and FF sequences are stored in
624 Funge-space instead of causing the dimension counters to be reset and
625 incremented. The data is stored all in one row.
628 =head2 get_char( vector )
631 Return the character stored in the torus at the specified location. If
632 the value is not between 0 and 255 (inclusive), get_char will return a
633 string that looks like "<np-0x4500>".
635 B</!\> As in Funge, code and data share the same playfield, the
636 character returned can be either an instruction B<or> raw data. No
637 guarantee is made that the return value is printable.
640 =head2 get_value( vector )
642 Return the number stored in the torus at the specified location. If
643 the value hasn't yet been set, it defaults to the ordinal value of a
646 B</!\> As in Funge, code and data share the same playfield, the
647 number returned can be either an instruction B<or> a data (or even
648 both... Eh, that's Funge! :o) ).
651 =head2 set_value( vector, value )
653 Write the supplied value in the torus at the specified location.
655 B</!\> As in Funge, code and data share the same playfield, the
656 number stored can be either an instruction B<or> a data (or even
657 both... Eh, that's Funge! :o) ).
660 =head2 move_ip_forward( ip )
662 Move the given ip forward, according to its delta.
665 =head2 wrap( position, delta )
667 Handle LaheySpace wrapping, if necessary.
670 =head2 rectangle( start, size )
672 Return a string containing the data/code in the specified rectangle.
674 Note that for useful data to be returned, the "size" vector must contain at
675 least a "1" for each component. After all, a world of 10 width, 10 length
676 but zero height would contain 0 planes. So, if any components of the
677 "size" vector do contain a 0, "" is returned.
680 =head2 labels_lookup( )
682 Parse the Lahey space to find sequences such as C<;:(\w[^\s;])[^;]*;>
683 and return a hash reference whose keys are the labels and the values
684 an anonymous array with two vectors: a vector describing the absolute
685 position of the character B<just after> the trailing C<;>, and a
686 vector describing the velocity that lead to this label.
688 This method will only look in the cardinal directions; west, east,
689 north, south, up, down and so forth.
691 This allow to define some labels in the source code, to be used by
692 C<Inline::Befunge> (and maybe some extensions).
699 Returns a Vector object, pointing at the beginning of the torus.
700 If nothing has been stored to a negative offset, this Vector will
701 point at the origin (0,0).
706 Returns a Vector object, pointing at the end of the torus.
707 This is usually the largest position which has been written to.
710 =head1 PRIVATE METHODS
712 =head2 _labels_try( start, delta )
714 Try in the specified direction if the funge space matches a label
715 definition. Return undef if it wasn't a label definition, or the name
716 of the label if it was a valid label.
721 None known. Please inform me if you find one.
726 L<Language::Befunge::Storage::Generic::Vec::XS>, L<Language::Befunge>.
731 Mark Glines, E<lt>infinoid@cpan.orgE<gt>
732 Jerome Quelin, E<lt>jquelin@cpan.orgE<gt>
734 Development is discussed on E<lt>language-befunge@mongueurs.netE<gt>
737 =head1 COPYRIGHT & LICENSE
739 Copyright (c) 2001-2008 Jerome Quelin, all rights reserved.
741 This program is free software; you can redistribute it and/or modify
742 it under the same terms as Perl itself.