The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/local/bin/perl -w
no strict qw(vars);
use strict qw(refs);
use strict qw(subs);

require 5.001;
package LLg;
use Lex;
use Carp;
sub new {		
  my $package = shift; 
  my @args = @_;
  my $ref;
  my $pre;
  my $post;
  my $elt;
				# user interface 
  ARGS:while (1) {	
      $elt = pop(@args);  
      $ref = ref($elt);
      if ($ref and $ref eq 'CODE') { # find anonymous routines   
	if ($#args >= 0 and $args[$#args] eq 'post') { # post => sub {...}
	  pop(@args);
	  $post = $elt;
	} elsif ($#args >= 0 and $args[$#args] eq 'pre') { # pre => sub {...}
	  pop(@args);
	  $post = $elt;
	} elsif (not defined($post)) {	# 
	  $post = $elt;
	} elsif ($post) {	# sub {...}, sub {...}
	  $pre = $post;
	  $post = $elt;
	}
      } else {
	push(@args, $elt);
	last ARGS;
      }
    }

  if ($package eq 'Do' and not defined($post)) {
    croak("$package object must have one anonymous routine as argument");
  }

  @args = ([ @args ],  ($pre or $post) ? ($post, $pre) : undef);

  # Number of objects
  if ($package ne 'Do') {
    if ($package eq 'Hook') {
      if ($#{$args[0]} > 0) {
	croak("$package object must have just one object as argument");
      }
    } elsif ($#{$args[0]} < 0) {
      croak("$package object must have at least one object as argument");
    }
    # Object type
    foreach (@{$args[0]}) {
      $ref = ref($_);
      if ($ref ne 'REF' and 
	  $ref ne 'SCALAR' and
	  $ref ne 'ARRAY') {
	croak("$_ isn't an ARRAY or a SCALAR reference");
      }
    }
  }
  bless [1, @args], $package;
}
sub pre { $_[0][3] }
sub post { $_[0][2] }
sub probe { print STDERR ref(shift), "\t", @_, "\n" }
sub status { defined($_[1]) ? $_[0]->[0] = $_[1] : $_[0]->[0] } 

package Hook;
@Hook::ISA = qw(LLg);

sub inherited { @_h }
sub next {
    my $self = shift;
    my $obj = ${${$self}[1]}[0];

    # Processing of inherited Attributes 
    # function: "pre" action
    # parameters: inherited attributes
    # return: inherited attributes
    local @_h = @_;  
    my $pre = $self->pre;
    if (defined($pre)) {	
      @_h = &{$pre}($self, @_);
    }
    # Processing of Sub-objects 
    # function: next
    # parameters: inherited attributes
    # return: transformed synthetized attributes
    local @_s = $$obj->next(@_h);

    # Semantic
    # function: semantic action
    # parameters: synthetized attributes
    # return: synthetized attributes
    if (not ($$obj->status)) {
	$self->status(0);
	();		
    } else {
      $self->status(1);
      my $post = $self->post;
      if (defined($post)) {
	&{$post}($self, @{_s});
      } else {
	@{_s}; 
      }
    } 
}
package And;
@And::ISA = qw(LLg);
sub inherited { @_h }
sub next {
    my $self = shift;
    my @objects = @{${$self}[1]};
    my @return;
    
    local @_h = @_;  
    my $pre = $self->pre;
    if (defined($pre)) {	
      @_h = &{$pre}($self, @_);
    }
    my $status = 1;
    my $obj;
    local @_s;
  OBJ:foreach $obj (@objects) { # sub-objects
      @return = $$obj->next(@_h);
      if (not $$obj->status) {
	$status = 0;
	last OBJ;
      } else {
	push(@_s, @return) if $#return >= 0;
      }
    }
    if ($status) { 
      $self->status(1);
      my $post = $self->post;
      if (defined($post)) {
	&{$post}($self, @_s);
      } else {
	@_s;
      }
    } else {
      $self->status(0);
      ();
    }
}
package Or;
@Or::ISA= qw(LLg);
my $reader = $Lex::reader;	# Default reader

sub inherited { @_h }
sub next {
    my $self = shift;
    my @objects = @{${$self}[1]};

    local(@_h) = @_;  
    my $pre = $self->pre;
    if (defined($pre)) {	
      @_h = &{$pre}($self, @_);
    }

    local @_s;
    my $obj;
    my @return;
    $Lex::reader->push;
 OBJ:foreach $obj (@objects) { 
    @return = $$obj->next(@_h);
    if ($$obj->status) {
      @_s = @return;
      $self->status(1);
      last OBJ;
    } else {			# failure!
      $Lex::reader->restore;	# backtracking
      $self->status(0);
    }
  } 
    $Lex::reader->pop;
    if ($self->status) {
      my $post = $self->post;
      if (defined($post)) {
	&{$post}($self, @_s);
      } else {
	@_s;
      }
    } else {
	();
    }
}
package Any;		
@Any::ISA= qw(LLg); 
my $reader = $Lex::reader;	# Default reader

sub inherited { @_h }
sub next {
    my $self = shift;
    my @objects = @{${$self}[1]};

    # action before
    local @_h = @_;  
    my $pre = $self->pre;
    if (defined($pre)) {	
      @_h = &{$pre}($self, @_);
    }

    $self->status(1);
    local @_s;
    my @return;
    my @tmpReturn;
    my $post = $self->post;
    my $obj;
  OBJ:while (1) {
      $Lex::reader->push;
      foreach $obj (@objects) {
	@return = $$obj->next(@_h);
	if (not $$obj->status) {
	  $Lex::reader->restore;
	  last OBJ;
	} 
	push(@_s, @return) if $#return >= 0;
      }
      $Lex::reader->pop;
      push(@tmpReturn, @_s);
      @_s = ();
  }
    $Lex::reader->pop;
    $self->status(1);
    @_s = @tmpReturn;
    if (defined($post)) {
      &{$post}($self, @_s);
    } else {
      @_s;
    }
}
package Opt;
@Opt::ISA = qw(LLg); 
my $reader = $Lex::reader;	# Default reader

sub inherited { @_h }
sub next {
  my $self = shift;
  my @objects = @{${$self}[1]};

  # action before
 local @_h = @_;  
  my $pre = $self->pre;
  if (defined($pre)) {	
    @_h = &{$pre}($self, @_);
  }
  local @_s;
  my @return;
  my @tmpReturn;
  my $post = $self->post;
  my $obj;
  $Lex::reader->push;
  OBJ:foreach $obj (@objects) {
      @return = $$obj->next(@_h);
      if (not $$obj->status) {
	$Lex::reader->restore;	
	$self->status(1);	
	@_s = ();
	last OBJ;
      } 
      push(@_s, @return) if $#return >= 0;
    }
  $Lex::reader->pop;
  $self->status(1);  
  if (defined($post)) {
    &{$post}($self, @_s);
  } else {
    @_s;
  }
}
package Do;
@Do::ISA = qw(LLg);
sub inherited { @_h }
sub next { 
  my $self = shift;
  local @_h = @_;
  &{$self->post}($self, @_s);
}
1;
__END__