Acme-Cow-Interpreter
view release on metacpan or search on metacpan
lib/Acme/Cow/Interpreter.pm view on Meta::CPAN
# -*- mode: perl; coding: us-ascii-unix; -*-
=pod
=head1 NAME
Acme::Cow::Interpreter - Cow programming language interpreter
=head1 SYNOPSIS
use Acme::Cow::Interpreter;
my $cow = Acme::Cow::Interpreter -> new();
$cow -> parse_file($file);
$cow -> execute();
=head1 ABSTRACT
This module implements an interpreter for the Cow programming language.
=head1 DESCRIPTION
This module implements an interpreter for the Cow programming language. The
Cow programming language is a so-called esoteric programming language, with
only 12 commands.
=cut
package Acme::Cow::Interpreter;
use strict; # restrict unsafe constructs
use warnings; # control optional warnings
use Carp;
our $VERSION = '0.02';
# This hash maps each of the 12 command (used in the source code) to the
# corresponding numerical code, from 0 to 11.
my $cmd2code =
{
moo => 0,
mOo => 1,
moO => 2,
mOO => 3,
Moo => 4,
MOo => 5,
MoO => 6,
MOO => 7,
OOO => 8,
MMM => 9,
OOM => 10,
oom => 11,
};
# This array maps each of the 12 numerical codes to the corresponding
# command (used in source code).
my $code2cmd =
[
'moo',
'mOo',
'moO',
'mOO',
'Moo',
'MOo',
'MoO',
'MOO',
'OOO',
'MMM',
'OOM',
'oom',
];
# This regular expression matches all the 12 valid commands.
my $cmd_regex = '(?:[Mm][Oo][Oo]|MMM|OO[MO]|oom)';
=pod
=head1 METHODS
=over 4
=item new()
Return a new Cow interpreter.
=cut
sub new {
my $proto = shift;
my $protoref = ref $proto;
my $class = $protoref || $proto;
my $name = 'new';
# Check how the method is called.
croak "$name() is a class method, not an instance/object method"
if $protoref;
# The new self.
my $self = {};
# Bless the reference into an object.
bless $self, $class;
# Initialize it. The return value of init() is the object itself.
$self -> init();
}
=pod
lib/Acme/Cow/Interpreter.pm view on Meta::CPAN
unless $selfref;
# Check number of arguments.
#croak "$name(): Not enough input arguments" if @_ < 0;
croak "$name(): Too many input arguments" if @_ > 0;
my $copy = {};
for my $key (keys %$self) {
my $ref = ref $self -> {$key};
if ($ref eq 'ARRAY') {
@{ $copy -> {$key} } = @{ $self -> {$key} };
} else {
$copy -> {$key} = $self -> {$key};
}
}
# Bless the copy into an object.
bless $copy, $class;
}
=pod
=item parse_string( STRING )
Parses the given string and stores the resulting list of codes in the
object. The return value is the object itself.
=cut
sub parse_string {
my $self = shift;
my $selfref = ref $self;
my $class = $selfref || $self;
my $name = 'parse_string';
# Check how the method is called.
croak "$name() is an instance/object method, not a class method"
unless $selfref;
# Check number of arguments.
croak "$name(): Not enough input arguments" if @_ < 1;
croak "$name(): Too many input arguments" if @_ > 1;
# There is no way the parser can fail. The worst thing that could happen
# is that there are no commands in the string.
my $string = shift; croak "$name(): Input argument is undefined"
unless defined $string;
# Reset, i.e., initialize, the invocand object.
$self -> init();
# Find the string commands, and convert them to numerical codes.
$self -> {prog} = [
map { $cmd2code -> {$_} }
$string =~ /($cmd_regex)/go
];
return $self;
}
=pod
=item parse_file( FILENAME )
Parses the contents of the given file and stores the resulting list of codes
in the object. The return value is the object itself.
=cut
sub parse_file {
my $self = shift;
my $selfref = ref $self;
my $class = $selfref || $self;
my $name = 'parse_file';
# Check how the method is called.
croak "$name() is an instance/object method, not a class method"
unless $selfref;
# Check number of arguments.
croak "$name(): Not enough input arguments" if @_ < 1;
croak "$name(): Too many input arguments" if @_ > 1;
# Reset, i.e., initialize, the invocand object.
$self -> init();
# Get the file name argument.
my $file = shift;
open FILE, $file or croak "$file: can't open file for reading: $!";
# Iterate over each line, find the string commands, and convert them to
# numerical codes.
while (<FILE>) {
push @{ $self -> {prog} },
map { $cmd2code -> {$_} }
/($cmd_regex)/go;
}
close FILE or croak "$file: can't close file after reading: $!";
return $self;
}
=pod
=item dump_mem()
Returns a nicely formatted string showing the current memory state.
=cut
sub dump_mem {
my $self = shift;
my $selfref = ref $self;
my $class = $selfref || $self;
my $name = 'dump_mem';
# Check how the method is called.
croak "$name() is an instance/object method, not a class method"
unless $selfref;
# Check number of arguments.
#croak "$name(): Not enough input arguments" if @_ < 0;
croak "$name(): Too many input arguments" if @_ > 0;
my $mem = $self -> {mem};
my $mem_pos = $self -> {mem_pos};
my $reg = $self -> {reg};
my $str = '';
# Print the contents of the memory, showing the block which the memory
# points at.
for (my $i = $#$mem ; $i >= 0 ; -- $i) {
$str .= sprintf "Memory block %6u: %12d", $i, $mem->[$i];
if ($i == $mem_pos) {
$str .= " <<<";
}
$str .= "\n";
}
# Print the contents of the register.
$str .= "\n";
$str .= sprintf "Register block: %17s", defined $reg ? $reg : '<undef>';
$str .= "\n";
return $str;
}
=pod
( run in 1.388 second using v1.01-cache-2.11-cpan-fe3c2283af0 )