# # This software is Copyright 2005 by Elsevier Inc. You may use it # under the terms of the license at http://perl.plover.com/hop/LICENSE.txt . # ### ### Type.pm ### ## Chapter 9 section 4.3 package Type; sub new { my ($old, $name, $parent) = @_; my $class = ref $old || $old; my $self = {N => $name, P => $parent, C => [], O => {}, D => [], }; bless $self => $class; } package Type::Scalar; @Type::Scalar::ISA = 'Type'; sub is_scalar { 1 } sub add_constraint { die "Added constraint to scalar type"; } sub add_subfeature { die "Added subfeature to scalar type"; } package Type; sub is_scalar { 0 } sub parent { $_[0]{P} } sub subfeature { my ($self, $name, $nocroak) = @_; return $self unless defined $name; my ($basename, $suffix) = split /\./, $name, 2; if (exists $_[0]{O}{$basename}) { return $_[0]{O}{$basename}->subfeature($suffix); } elsif (my $parent = $self->parent) { $parent->subfeature($name); } elsif ($nocroak) { return; } else { Carp::croak("Asked for nonexistent subfeature '$name' of type '$self->{N}'"); } } sub has_subfeature { my ($self, $name) = @_; defined($self->subfeature($name, "don't croak")); } ## Chapter 9 section 4.3.2 sub constraints { my $self = shift; my @constraints = @{$self->{C}}; my $p = $self->parent; if (defined $p) { push @constraints, @{$p->constraints} } while (my ($name, $type) = each %{$self->{O}}) { my @subconstraints = @{$type->constraints}; push @constraints, map $_->qualify($name), @subconstraints; } \@constraints; } sub constraint_set { my $self = shift; Constraint_Set->new(@{$self->constraints}); } sub intrinsic_constraints { my $constraints = $_[0]->constraints; Intrinsic_Constraint_Set->new(@$constraints); } sub qualified_intrinsic_constraints { $_[0]->intrinsic_constraints->qualify($_[1]); } ## Chapter 9 section 4.3.2 sub all_leaf_subfeatures { my $self = shift; my @all; my %base = $self->subfeatures; while (my ($name, $type) = each %base) { push @all, map {$_ eq "" ? $name : "$name.$_"} $type->all_leaf_subfeatures; } @all; } sub synthetic_constraints { my @subfeatures = $_[0]->all_leaf_subfeatures; Synthetic_Constraint_Set->new(map {$_ => Constraint->new($_ => 1)} @subfeatures ); } sub qualified_synthetic_constraints { $_[0]->synthetic_constraints->qualify($_[1]); } sub add_drawable { my ($self, $drawable) = @_; push @{$self->{D}}, $drawable; } sub subfeatures { my $self = shift; my %all; while ($self) { %all = (%{$self->{O}}, %all); $self = $self->parent; } %all; } sub drawables { my ($self) = @_; return @{$self->{D}} if $self->{D} && @{$self->{D}}; if (my $p = $self->parent) { my @drawables = $p->drawables; return @drawables if @drawables; } my %subfeature = $self->subfeatures; my @drawables = grep ! $subfeature{$_}->is_scalar, keys %subfeature; @drawables; } sub add_subfeature { my ($self, $name, $type) = @_; $self->{O}{$name} = $type; } sub add_constraints { my ($self, @values) = @_; for my $value (@values) { next unless $value->kindof eq 'FEATURE'; push @{$self->{C}}, $value->intrinsic->constraints, $value->synthetic->constraints; } } sub draw { my ($self, $env) = @_; unless ($env) { my $equations = $self->constraint_set; my %solutions = $equations->values; $env = Environment->new(%solutions); } for my $name ($self->drawables) { if (ref $name) { # actually a coderef, not a name $name->($env); } else { my $type = $self->subfeature($name); my $subenv = $env->subset($name); $type->draw($subenv); } } } 1;