Acme-Chef
view release on metacpan or search on metacpan
lib/Acme/Chef/Recipe.pm view on Meta::CPAN
=item execute
Executes the recipe (program). First argument should be a reference to a
hash of sous-recipes.
=cut
sub execute {
my $self = shift;
my $recipes = shift;
$self->compile() unless $self->{compiled};
my @loop_stack;
my $max_pos = $#{$self->{method}};
my $exec_pos = 0;
while (1) {
my $next_method = $self->{method}->[$exec_pos];
# print ' ' x scalar(@loop_stack), join(',', @$next_method),"\n";
my $return = $Commands{$next_method->[0]}->($self, $next_method);
last if $return eq 'halt';
if ( $return =~ /^recurse\.([\-\w][\-\w ]*)/ ) {
exists $recipes->{$1}
or croak "Invalid recipe '$1' specified for recursion.";
my $clone = $self->new();
my $sous_recipe = $recipes->{$1}->new(
bowls => $clone->{bowls},
dishes => $clone->{dishes},
);
my $sous_done = $sous_recipe->execute( $recipes );
$self->output( $sous_done->output() );
$self -> {bowls} -> [0]
-> put( $sous_done -> first_bowl() -> new() -> pour() );
} elsif ( $return =~ /^loop\.([^\.]+)\.([^\.]+)/ ) {
my $verb = $1;
my $ingr = $2;
push @loop_stack, $verb;
if ( not $self -> {ingredients} -> {$ingr} -> value() ) {
pop @loop_stack;
$exec_pos = $self -> {loops} -> {$verb} -> {end};
}
} elsif ( $return =~ /^endloop\.([^\.]+)/ ) {
my $verb = $1;
$exec_pos = $self -> {loops} -> {$verb} -> {start} - 1;
} elsif ( $return =~ /^break/ ) {
my $verb = pop @loop_stack;
$exec_pos = $self -> {loops} -> {$verb} -> {end};
}
$exec_pos++;
last if $exec_pos > $max_pos;
}
if ( $self->{serves} ) {
foreach my $serve ( 0..($self->{serves}-1) ) {
last if $serve > $#{$self->{dishes}};
my $string = $self->{dishes}->[$serve]->print();
$self->{output} .= $string;
}
}
return $self;
}
=item first_bowl
Returns the first bowl of the recipe.
=cut
sub first_bowl {
my $self = shift;
return $self->{bowls}->[0];
}
=item require_ingredient
First argument must be an ingredient object. Second may be a string indicating
the location of the requirement. Throws a fatal error if the ingredient is not
present.
=cut
sub require_ingredient {
my $self = shift;
my $ingredient = shift;
my $sub = shift;
(defined $ingredient and exists $self->{ingredients}{$ingredient})
or croak "Unknown ingredient '".(defined$ingredient?$ingredient:'<undefined>').
"' required for recipe '$self->{name}'".
(defined $sub?" in '$sub'":'').".";
return $self;
}
=item output
Mutator for the Recipe output.
=cut
sub output {
lib/Acme/Chef/Recipe.pm view on Meta::CPAN
my $value;
if (s/^[ ]*(\d+)[ ]//) {
$value = $1;
} else {
$value = undef;
}
my $measure_type = '';
foreach my $type ( keys %Acme::Chef::Ingredient::MeasureTypes ) {
if ( s/^\Q$type\E[ ]// ) {
$measure_type = $type;
last;
}
}
my $measure = '';
foreach my $meas ( keys %Acme::Chef::Ingredient::Measures ) {
next if $meas eq '';
if ( s/^\Q$meas\E[ ]// ) {
$measure = $meas;
last;
}
}
/[ ]*([\-\w][\- \w]*)[ ]*$/
or croak "Invalid ingredient specification (ingredient no. $ingredient_no, name).";
my $ingredient_name = $1;
my $ingredient = Acme::Chef::Ingredient->new(
name => $ingredient_name,
value => $value,
measure => $measure,
measure_type => $measure_type,
);
$ingredients{$ingredient_name} = $ingredient;
}
$self->{ingredients} = \%ingredients;
$self->{method} =~ s/\s+/ /g;
my @steps = split /\s*\.\s*/, $self->{method};
shift @steps; # remove "Method."
my $step_no = 0;
foreach my $step (@steps) {
$step_no++;
foreach my $grammar (@GrammarOrder) {
my @res = $Grammars{$grammar}->($self, $step);
@res or next;
if ( $res[0] eq 'verb' ) {
my $verb = $res[1];
my $ingr = $res[2];
$self->{loops}->{$verb} = {start => ($step_no-1), test => $ingr};
} elsif ( $res[0] eq 'until_verbed' ) {
my $verb = $res[1];
exists $self->{loops}->{$verb}
or croak "Loop end without loop start '$verb'.";
$self->{loops}->{$verb}->{end} = $step_no - 1;
}
$step = [@res];
last;
}
croak "Invalid method step (step no. $step_no): '$step'."
if not ref $step eq 'ARRAY';
}
if ( grep { not exists $self->{loops}{$_}{end} } keys %{$self->{loops}} ) {
croak "Not all loop starting points have matching ends.";
}
$self->{method} = \@steps;
$self->{compiled} = 1;
return $self;
}
__END__
=back
=head1 AUTHOR
Steffen Mueller.
Chef designed by David Morgan-Mar.
=head1 COPYRIGHT AND LICENSE
Copyright (c) 2002-2008 Steffen Mueller. All rights reserved. This program is
free software; you can redistribute it and/or modify it under the same
terms as Perl itself.
Author can be reached at chef-module at steffen-mueller dot net
=cut
( run in 2.338 seconds using v1.01-cache-2.11-cpan-437f7b0c052 )