fix handprint: should be a lone number instead of a 0gnirts
[language-befunge.git] / lib / Language / Befunge / Interpreter.pm
blobd68a0f97c9af7f04b4b12b98397c042e301a3077
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::Interpreter;
11 use 5.010;
13 use strict;
14 use warnings;
16 use Carp;
17 use Language::Befunge::IP;
18 use UNIVERSAL::require;
20 use base qw{ Class::Accessor::Fast };
21 __PACKAGE__->mk_accessors( qw{ storage _wrapping input } );
23 # Public variables of the module.
24 $| = 1;
27 # -- CONSTRUCTOR
31 # my $interpreter = LBI->new( $opts )
33 # Create a new funge interpreter. One can pass some options as a hash
34 # reference, with the following keys:
35 # - file: the filename to read funge code from (default: blank storage)
36 # - syntax: the tunings set (default: 'befunge98')
37 # - dims: the number of dimensions
38 # - ops: the Ops subclass used in this interpreter
39 # - storage: the Storage subclass used in this interpreter
40 # - wrapping: the Wrapping subclass used in this interpreter
42 # Usually, the "dims", "ops", "storage" and "wrapping" keys are left
43 # undefined, and are implied by the "syntax" key.
45 # Depending on the value of syntax will change the interpreter
46 # internals: set of allowed ops, storage implementation, wrapping. The
47 # following values are recognized for 'syntax' (with in order: the
48 # number of dimensions, the set of operation loaded, the storage
49 # implementation and the wrapping implementation):
51 # - unefunge98: 1, LBO:Unefunge98, LBS:Generic::AoA, LBW:LaheySpace
52 # - befunge98: 2, LBO:Befunge98, LBS:2D:Sparse, LBW:LaheySpace
53 # - trefunge98: 3, LBO:GenericFunge98, LBS:Generic::AoA, LBW:LaheySpace
54 # - 4funge98: 4, LBO:GenericFunge98, LBS:Generic::AoA, LBW:LaheySpace
55 # - 5funge98: 5, LBO:GenericFunge98, LBS:Generic::AoA, LBW:LaheySpace
56 # ...and so on.
59 # If none of those values suit your needs, you can pass the value
60 # 'custom' and in that case you're responsible for also giving
61 # appropriate values for the keys 'dims', 'ops', 'storage', 'wrapping'.
62 # Note that those values will be ignored for all syntax values beside
63 # 'custom'.
65 sub new {
66 my ($class, $opts) = @_;
68 $opts //= { dims => 2 };
69 unless(exists($$opts{syntax})) {
70 $$opts{dims} //= 2;
71 croak("If you pass a 'dims' attribute, it must be numeric.")
72 if $$opts{dims} =~ /\D/;
73 my %defaults = (
74 1 => 'unefunge98',
75 2 => 'befunge98',
76 3 => 'trefunge98',
78 if(exists($defaults{$$opts{dims}})) {
79 $$opts{syntax} = $defaults{$$opts{dims}};
80 } else {
81 $$opts{syntax} = $$opts{dims} . 'funge98';
85 # select the classes to use, depending on the wanted syntax.
86 my $lbo = 'Language::Befunge::Ops::';
87 my $lbs = 'Language::Befunge::Storage::';
88 my $lbw = 'Language::Befunge::Wrapping::';
89 given ( $opts->{syntax} ) {
90 when ('unefunge98') {
91 $opts->{dims} = 1 unless defined $opts->{dims};
92 $opts->{ops} = $lbo . 'Unefunge98' unless defined $opts->{ops};
93 $opts->{storage} = $lbs . 'Generic::AoA' unless defined $opts->{storage};
94 $opts->{wrapping} = $lbw . 'LaheySpace' unless defined $opts->{wrapping};
96 when ('befunge98') {
97 $opts->{dims} = 2 unless defined $opts->{dims};
98 $opts->{ops} = $lbo . 'Befunge98' unless defined $opts->{ops};
99 $opts->{storage} = $lbs . '2D::Sparse' unless defined $opts->{storage};
100 $opts->{wrapping} = $lbw . 'LaheySpace' unless defined $opts->{wrapping};
102 when ('trefunge98') {
103 $opts->{dims} = 3 unless defined $opts->{dims};
104 $opts->{ops} = $lbo . 'GenericFunge98' unless defined $opts->{ops};
105 $opts->{storage} = $lbs . 'Generic::AoA' unless defined $opts->{storage};
106 $opts->{wrapping} = $lbw . 'LaheySpace' unless defined $opts->{wrapping};
108 when (/(\d+)funge98$/) { # accept values like "4funge98"
109 $opts->{dims} = $1 unless defined $opts->{dims};
110 $opts->{ops} = $lbo . 'GenericFunge98' unless defined $opts->{ops};
111 $opts->{storage} = $lbs . 'Generic::AoA' unless defined $opts->{storage};
112 $opts->{wrapping} = $lbw . 'LaheySpace' unless defined $opts->{wrapping};
114 default { croak "syntax '$opts->{syntax}' not recognized." }
117 # load the classes (through UNIVERSAL::require)
118 $opts->{ops}->use;
119 $opts->{storage}->use;
120 $opts->{wrapping}->use;
122 # create the object
123 my $wrapping = $opts->{wrapping}->new;
124 my $self = {
125 dimensions => $opts->{dims},
126 storage => $opts->{storage}->new( $opts->{dims}, Wrapping => $wrapping ),
127 file => "STDIN",
128 input => '',
129 params => [],
130 retval => 0,
131 DEBUG => 0,
132 curip => undef,
133 ops => $opts->{ops}->get_ops_map,
134 ips => [],
135 newips => [],
136 handprint => 'JQBF', # the official handprint
137 _wrapping => $wrapping,
139 bless $self, $class;
141 # read the file if needed.
142 defined($opts->{file}) and $self->read_file( $opts->{file} );
144 # return the object.
145 return $self;
150 # -- ACCESSORS
152 # The following is a list of attributes of a Language::Befunge
153 # object. For each of them, a method C<get_foobar> and C<set_foobar>
154 # exists, which does what you can imagine - and if you can't, then i
155 # wonder why you are reading this! :-)
157 # get_curip() / set_curip()
158 # the current Instruction Pointer processed (a L::B::IP object)
160 # get_DEBUG() / set_DEBUG()
161 # wether the interpreter should output debug messages (a boolean)
163 # get_dimensions() / set_dimensions()
164 # the number of dimensions this interpreter works in.
166 # get_file() / set_file()
167 # the script filename (a string)
169 # get_handprint() / set_handprint()
170 # the handprint of the interpreter
172 # get_ips() / set_ips()
173 # the current set of IPs travelling in the Lahey space (an array
174 # reference)
176 # get_newips() / set_newips()
177 # the set of IPs that B<will> travel in the Lahey space B<after> the
178 # current tick (an array reference)
180 # get_ops() / set_ops()
181 # the current supported operations set.
183 # get_params() / set_params()
184 # the parameters of the script (an array reference)
186 # get_retval() / set_retval()
187 # the current return value of the interpreter (an integer)
189 BEGIN {
190 my @attrs = qw[ dimensions file params retval DEBUG curip ips newips ops handprint ];
191 foreach my $attr ( @attrs ) {
192 my $code = qq[ sub get_$attr { return \$_[0]->{$attr} } ];
193 $code .= qq[ sub set_$attr { \$_[0]->{$attr} = \$_[1] } ];
194 eval $code;
199 # -- PUBLIC METHODS
201 # - Utilities
205 # move_ip( $ip [,regex] )
207 # Move $ip according to its delta on the storage.
209 # If a regex ( a C<qr//> object ) is specified, then $ip will move as
210 # long as the pointed character match the supplied regex.
212 # Example: given the code C<;foobar;> (assuming the IP points on the
213 # first C<;>) and the regex C<qr/[^;]/>, the IP will move in order to
214 # point on the C<r>.
216 sub move_ip {
217 my ($self, $ip, $re) = @_;
218 my $storage = $self->storage;
220 if ( defined $re ) {
221 my $orig = $ip->get_position;
222 # moving as long as we did not reach the condition.
223 while ( $storage->get_char($ip->get_position) =~ $re ) {
224 $self->_move_ip_forward($ip);
225 $self->abort("infinite loop")
226 if $ip->get_position == $orig;
229 # we moved one char too far.
230 $ip->dir_reverse;
231 $self->_move_ip_forward($ip);
232 $ip->dir_reverse;
234 } else {
235 # moving one step beyond...
236 $self->_move_ip_forward($ip);
242 # abort( reason )
244 # Abort the interpreter with the given reason, as well as the current
245 # file and coordinate of the offending instruction.
247 sub abort {
248 my $self = shift;
249 my $file = $self->get_file;
250 my $v = $self->get_curip->get_position;
251 croak "$file $v: ", @_;
256 # debug( LIST )
258 # Issue a warning if the interpreter has DEBUG enabled.
260 sub debug {
261 my $self = shift;
262 $self->get_DEBUG or return;
263 warn @_;
268 # set_input( $string )
270 # Preload the input buffer with the given value.
272 sub set_input {
273 my ($self, $str) = @_;
274 $self->input($str);
279 # get_input( )
281 # Fetch a character of input from the input buffer, or else, directly
282 # from stdin.
285 sub get_input {
286 my $self = shift;
287 return substr($$self{input}, 0, 1, '') if length $self->input;
288 my $char;
289 my $rv = sysread(STDIN, $char, 1);
290 return $char if length $char;
291 return undef;
295 # - Code and Data Storage
298 # read_file( filename )
300 # Read a file (given as argument) and store its code.
302 # Side effect: clear the previous code.
304 sub read_file {
305 my ($self, $file) = @_;
307 # Fetch the code.
308 my $code;
309 open BF, "<$file" or croak "$!";
311 local $/; # slurp mode.
312 $code = <BF>;
314 close BF;
316 # Store code.
317 $self->set_file( $file );
318 $self->store_code( $code );
323 # store_code( code )
325 # Store the given code in the Lahey space.
327 # Side effect: clear the previous code.
329 sub store_code {
330 my ($self, $code) = @_;
331 $self->debug( "Storing code\n" );
332 $self->storage->clear;
333 $self->storage->store( $code );
337 # - Run methods
341 # run_code( [params] )
343 # Run the current code. That is, create a new Instruction Pointer and
344 # move it around the code.
346 # Return the exit code of the program.
348 sub run_code {
349 my $self = shift;
350 $self->set_params( [ @_ ] );
352 # Cosmetics.
353 $self->debug( "\n-= NEW RUN (".$self->get_file.") =-\n" );
355 # Create the first Instruction Pointer.
356 $self->set_ips( [ Language::Befunge::IP->new($$self{dimensions}) ] );
357 $self->set_retval(0);
359 # Loop as long as there are IPs.
360 $self->next_tick while scalar @{ $self->get_ips };
362 # Return the exit code.
363 return $self->get_retval;
368 # next_tick( )
370 # Finish the current tick and stop just before the next tick.
372 sub next_tick {
373 my $self = shift;
375 # Cosmetics.
376 $self->debug( "Tick!\n" );
378 # Process the set of IPs.
379 $self->set_newips( [] );
380 $self->process_ip while $self->set_curip( shift @{ $self->get_ips } );
382 # Copy the new ips.
383 $self->set_ips( $self->get_newips );
388 # process_ip( )
390 # Process the current ip.
392 sub process_ip {
393 my ($self, $continue) = @_;
394 $continue = 1 unless defined $continue;
395 my $ip = $self->get_curip;
397 # Fetch values for this IP.
398 my $v = $ip->get_position;
399 my $ord = $self->storage->get_value( $v );
400 my $char = $self->storage->get_char( $v );
402 # Cosmetics.
403 $self->debug( "#".$ip->get_id.":$v: $char (ord=$ord) Stack=(@{$ip->get_toss})\n" );
405 # Check if we are in string-mode.
406 if ( $ip->get_string_mode ) {
407 if ( $char eq '"' ) {
408 # End of string-mode.
409 $self->debug( "leaving string-mode\n" );
410 $ip->set_string_mode(0);
412 } elsif ( $char eq ' ' ) {
413 # A serie of spaces, to be treated as one space.
414 $self->debug( "string-mode: pushing char ' '\n" );
415 $self->move_ip( $ip, qr/ / );
416 $ip->spush( $ord );
418 } else {
419 # A banal character.
420 $self->debug( "string-mode: pushing char '$char'\n" );
421 $ip->spush( $ord );
424 } else {
425 # Not in string-mode.
426 if ( exists $self->get_ops->{$char} ) {
427 # Regular instruction.
428 my $meth = $self->get_ops->{$char};
429 $meth->($self);
431 } else {
432 # Not a regular instruction: reflect.
433 $self->debug( "the command value $ord (char='$char') is not implemented.\n");
434 $ip->dir_reverse;
438 if ($continue) {
439 # Tick done for this IP, let's move it and push it in the
440 # set of non-terminated IPs.
441 $self->move_ip( $self->get_curip );
442 push @{ $self->get_newips }, $ip unless $ip->get_end;
446 #-- PRIVATE METHODS
450 # $lbi->_move_ip_forward( $ip );
452 # move $ip one step further, according to its velocity. if $ip gets out
453 # of bounds, then a wrapping is performed (according to current
454 # interpreter wrapping implementation) on the ip.
456 sub _move_ip_forward {
457 my ($self, $ip) = @_;
458 my $storage = $self->storage;
460 # fetch the current position of the ip.
461 my $v = $ip->get_position;
462 my $d = $ip->get_delta;
464 # now, let's move the ip.
465 $v += $d;
467 if ( $v->bounds_check($storage->min, $storage->max) ) {
468 # within bounds - store new position.
469 $ip->set_position( $v );
470 } else {
471 # wrap needed - this will update the position.
472 $self->_wrapping->wrap( $storage, $ip );
478 __END__
480 =head1 CONSTRUCTOR
482 =head2 new( [filename, ] [ Key => Value, ... ] )
484 Create a new Befunge interpreter. As an optional first argument, you
485 can pass it a filename to read Funge code from (default: blank
486 torus). All other arguments are key=>value pairs. The following
487 keys are accepted, with their default values shown:
489 Dimensions => 2,
490 Syntax => 'befunge98',
491 Storage => 'laheyspace'
493 =head1 ACCESSORS
495 The following is a list of attributes of a Language::Befunge
496 object. For each of them, a method C<get_foobar> and C<set_foobar>
497 exists, which does what you can imagine - and if you can't, then i
498 wonder why you are reading this! :-)
500 =over 4
502 =item get_curip() / set_curip()
504 the current Instruction Pointer processed (a L::B::IP object)
506 =item get_DEBUG() / set_DEBUG()
508 wether the interpreter should output debug messages (a boolean)
510 =item get_dimensions() / set_dimensions()
512 the number of dimensions this interpreter works in.
514 =item get_file() / set_file()
516 the script filename (a string)
518 =item get_handprint() / set_handprint()
520 the handprint of the interpreter
522 =item get_ips() / set_ips()
524 the current set of IPs travelling in the Lahey space (an array
525 reference)
527 =item get_newips() / set_newips()
529 the set of IPs that B<will> travel in the Lahey space B<after> the
530 current tick (an array reference)
532 =item get_ops() / set_ops()
534 the current supported operations set.
536 =item get_params() / set_params()
538 the parameters of the script (an array reference)
540 =item get_retval() / set_retval()
542 the current return value of the interpreter (an integer)
544 =back
547 =head1 PUBLIC METHODS
549 =head2 Utilities
551 =over 4
553 =item move_ip( $ip [, $regex] )
555 Move the C<$ip> according to its delta on the storage.
557 If C<$regex> ( a C<qr//> object ) is specified, then C<$ip> will move as
558 long as the pointed character match the supplied regex.
560 Example: given the code C<;foobar;> (assuming the IP points on the
561 first C<;>) and the regex C<qr/[^;]/>, the IP will move in order to
562 point on the C<r>.
565 =item abort( reason )
567 Abort the interpreter with the given reason, as well as the current
568 file and coordinate of the offending instruction.
571 =item debug( LIST )
573 Issue a warning if the interpreter has DEBUG enabled.
576 =item set_input( $string )
578 Preload the input buffer with the given value.
581 =item get_input( )
583 Fetch a character of input from the input buffer, or else, directly
584 from stdin.
587 =back
591 =head2 Code and Data Storage
593 =over 4
595 =item read_file( filename )
597 Read a file (given as argument) and store its code.
599 Side effect: clear the previous code.
602 =item store_code( code )
604 Store the given code in the Lahey space.
606 Side effect: clear the previous code.
609 =back
613 =head2 Run methods
615 =over 4
617 =item run_code( [params] )
619 Run the current code. That is, create a new Instruction Pointer and
620 move it around the code.
622 Return the exit code of the program.
625 =item next_tick( )
627 Finish the current tick and stop just before the next tick.
630 =item process_ip( )
632 Process the current ip.
635 =back
638 =head1 TODO
640 =over 4
642 =item o
644 Write standard libraries.
646 =back
649 =head1 BUGS
651 Although this module comes with a full set of tests, maybe there are
652 subtle bugs - or maybe even I misinterpreted the Funge-98
653 specs. Please report them to me.
655 There are some bugs anyway, but they come from the specs:
657 =over 4
659 =item o
661 About the 18th cell pushed by the C<y> instruction: Funge specs just
662 tell to push onto the stack the size of the stacks, but nothing is
663 said about how user will retrieve the number of stacks.
665 =item o
667 About the load semantics. Once a library is loaded, the interpreter is
668 to put onto the TOSS the fingerprint of the just-loaded library. But
669 nothing is said if the fingerprint is bigger than the maximum cell
670 width (here, 4 bytes). This means that libraries can't have a name
671 bigger than C<0x80000000>, ie, more than four letters with the first
672 one smaller than C<P> (C<chr(80)>).
674 Since perl is not so rigid, one can build libraries with more than
675 four letters, but perl will issue a warning about non-portability of
676 numbers greater than C<0xffffffff>.
678 =back
681 =head1 ACKNOWLEDGEMENTS
683 I would like to thank Chris Pressey, creator of Befunge, who gave a
684 whole new dimension to both coding and obfuscating.
687 =head1 SEE ALSO
689 L<Language::Befunge>
692 =head1 AUTHOR
694 Jerome Quelin, E<lt>jquelin@cpan.orgE<gt>
696 Development is discussed on E<lt>language-befunge@mongueurs.netE<gt>
699 =head1 COPYRIGHT & LICENSE
701 Copyright (c) 2001-2008 Jerome Quelin, all rights reserved.
703 This program is free software; you can redistribute it and/or modify
704 it under the same terms as Perl itself.
707 =cut