[system] / trunk / pg / macros / contextLimitedVector.pl Repository:
ViewVC logotype

View of /trunk/pg/macros/contextLimitedVector.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3371 - (download) (as text) (annotate)
Tue Jul 12 22:39:56 2005 UTC (14 years, 6 months ago) by dpvc
File size: 7837 byte(s)
Make error messages potentially localizable (by making them use
sprintf-style strings rather than variable subtitution).

    1 loadMacros("Parser.pl");
    2 
    3 sub _contextLimitedVector_init {}; # don't load it again
    4 
    5 ##########################################################
    6 #
    7 #  Implements a context in which vectors can be entered,
    8 #  but no vector operations are permitted.  So students will
    9 #  be able to perform operations within the coordinates
   10 #  of the vectors, but not between vectors.
   11 #
   12 #  Vectors can still be entered either in < , , > or ijk format.
   13 #  Most of the complication here is to handle ijk format
   14 #  properly.  Each coordinate vector is allowed to appear
   15 #  only once, so we have to keep track of that, and allow
   16 #  SOME vector operations, but only when one term is
   17 #  one of the coordinate constants, or one of the formulas
   18 #  we've already OKed.
   19 #
   20 #  You control which format to use by setting the context
   21 #  to one of the following:
   22 #
   23 # Context("LimitedVector-coordinate");
   24 #       Context("LimitedVector-ijk");
   25 #       Context("LimitedVector");      # either one
   26 #
   27 
   28 #
   29 #  Handle common checking for BOPs
   30 #
   31 package LimitedVector::BOP;
   32 
   33 #
   34 #  Do original check and then if the operands are numbers, its OK.
   35 #  Otherwise, check if there is a duplicate constant from either term
   36 #  Otherwise, do an operator-specific check for if vectors are OK.
   37 #  Otherwise report an error.
   38 #
   39 sub _check {
   40   my $self = shift;
   41   my $super = ref($self); $super =~ s/LimitedVector/Parser/;
   42   &{$super."::_check"}($self);
   43   return if $self->checkNumbers;
   44   if ($self->{equation}{context}{flags}{vector_format} ne 'coordinate') {
   45     $self->checkConstants($self->{lop});
   46     $self->checkConstants($self->{rop});
   47     return if $self->checkVectors;
   48   }
   49   my $bop = $self->{def}{string} || $self->{bop};
   50   $self->Error("In this context, '%s' can only be used with Numbers",$bop)
   51     if $self->{equation}{context}{flags}{vector_format} eq 'coordinate';
   52   $self->Error("In this context, '%s' can only be used with Numbers or i,j and k",$bop);
   53 }
   54 
   55 #
   56 #  filled in by subclasses
   57 #
   58 sub checkVectors {return 0}
   59 
   60 #
   61 #  Check if a constant has been repeated
   62 #  (we maintain a hash that lists if one is below us in the parse tree)
   63 #
   64 sub checkConstants {
   65   my $self = shift; my $op = shift;
   66   my $duplicate = '';
   67   if ($op->class eq 'Constant') {
   68     return unless $op->{name} =~ m/^[ijk]$/;
   69     $duplicate = $op->{name} if $self->{ijk}{$op->{name}};
   70     $self->{ijk}{$op->{name}} = 1;
   71   } else {
   72     foreach my $x ('i','j','k') {
   73       $duplicate = $x if $self->{ijk}{$x} && $op->{ijk}{$x};
   74       $self->{ijk}{$x} = $self->{ijk}{$x} || $op->{ijk}{$x};
   75     }
   76   }
   77   Value::Error("The constant '%s' may appear only once in your formula",$duplicate)
   78     if $duplicate;
   79 }
   80 
   81 ##############################################
   82 #
   83 #  Now we get the individual replacements for the operators
   84 #  that we don't want to allow.  We inherit everything from
   85 #  the original Parser::BOP class, and just add the
   86 #  vector checks here.
   87 #
   88 
   89 package LimitedVector::BOP::add;
   90 our @ISA = qw(LimitedVector::BOP Parser::BOP::add);
   91 
   92 sub checkVectors {
   93   my $self = shift;
   94   return (($self->{lop}->class eq 'Constant' || $self->{lop}->class =~ m/[BU]OP/) &&
   95           ($self->{rop}->class eq 'Constant' || $self->{rop}->class =~ m/[BU]OP/));
   96 }
   97 
   98 ##############################################
   99 
  100 package LimitedVector::BOP::subtract;
  101 our @ISA = qw(LimitedVector::BOP Parser::BOP::subtract);
  102 
  103 sub checkVectors {
  104   my $self = shift;
  105   return (($self->{lop}->class eq 'Constant' || $self->{lop}->class =~ m/[BU]OP/) &&
  106           ($self->{rop}->class eq 'Constant' || $self->{rop}->class =~ m/[BU]OP/));
  107 }
  108 
  109 ##############################################
  110 
  111 package LimitedVector::BOP::multiply;
  112 our @ISA = qw(LimitedVector::BOP Parser::BOP::multiply);
  113 
  114 sub checkVectors {
  115   my $self = shift;
  116   return (($self->{lop}->class eq 'Constant' || $self->{lop}->type eq 'Number') &&
  117     ($self->{rop}->class eq 'Constant' || $self->{rop}->type eq 'Number'));
  118 }
  119 
  120 ##############################################
  121 
  122 package LimitedVector::BOP::divide;
  123 our @ISA = qw(LimitedVector::BOP Parser::BOP::divide);
  124 
  125 sub checkVectors {
  126   my $self = shift;
  127   my $bop = $self->{def}{string} || $self->{bop};
  128   $self->Error("In this context, '%s' can only be used with Numbers",$bop);
  129 }
  130 
  131 ##############################################
  132 ##############################################
  133 #
  134 #  Now we do the same for the unary operators
  135 #
  136 
  137 package LimitedVector::UOP;
  138 
  139 sub _check {
  140   my $self = shift;
  141   my $super = ref($self); $super =~ s/LimitedVector/Parser/;
  142   &{$super."::_check"}($self);
  143   return if $self->checkNumber;
  144   if ($self->{equation}{context}{flags}{vector_format} ne 'coordinate') {
  145     LimitedVector::BOP::checkConstants($self,$self->{op});
  146     return if $self->checkVector;
  147   }
  148   my $uop = $self->{def}{string} || $self->{uop};
  149   $self->Error("In this context, '%s' can only be used with Numbers",$uop)
  150     if $self->{equation}{context}{flags}{vector_format} eq 'coordinate';
  151   $self->Error("In this context, '%s' can only be used with Numbers or i,j and k",$uop);
  152 }
  153 
  154 sub checkVector {return 0}
  155 
  156 ##############################################
  157 
  158 package LimitedVector::UOP::plus;
  159 our @ISA = qw(LimitedVector::UOP Parser::UOP::plus);
  160 
  161 sub checkVector {return shift->{op}->class eq 'Constant'}
  162 
  163 ##############################################
  164 
  165 package LimitedVector::UOP::minus;
  166 our @ISA = qw(LimitedVector::UOP Parser::UOP::minus);
  167 
  168 sub checkVector {return shift->{op}->class eq 'Constant'}
  169 
  170 ##############################################
  171 ##############################################
  172 #
  173 #  Absolute value does vector norm, so we
  174 #  trap that as well.
  175 #
  176 
  177 package LimitedVector::List::AbsoluteValue;
  178 our @ISA = qw(Parser::List::AbsoluteValue);
  179 
  180 sub _check {
  181   my $self = shift;
  182   $self->SUPER::_check;
  183   return if $self->{coords}[0]->type eq 'Number';
  184   $self->Error("Vector norm is not allowed in this context");
  185 }
  186 
  187 ##############################################
  188 
  189 package LimitedVector::List::Vector;
  190 our @ISA = qw(Parser::List::Vector);
  191 
  192 sub _check {
  193   my $self = shift;
  194   $self->SUPER::_check;
  195   return if $self->{equation}{context}{flags}{vector_format} ne 'ijk';
  196   $self->Error("Vectors must be given in the form 'ai+bj+ck' in this context");
  197 }
  198 
  199 ##############################################
  200 ##############################################
  201 
  202 package main;
  203 
  204 #
  205 #  Now build the new context that calls the
  206 #  above classes rather than the usual ones
  207 #
  208 
  209 $context{LimitedVector} = Context("Vector");
  210 $context{LimitedVector}->operators->set(
  211    '+' => {class => 'LimitedVector::BOP::add'},
  212    '-' => {class => 'LimitedVector::BOP::subtract'},
  213    '*' => {class => 'LimitedVector::BOP::multiply'},
  214   '* ' => {class => 'LimitedVector::BOP::multiply'},
  215   ' *' => {class => 'LimitedVector::BOP::multiply'},
  216    ' ' => {class => 'LimitedVector::BOP::multiply'},
  217    '/' => {class => 'LimitedVector::BOP::divide'},
  218   ' /' => {class => 'LimitedVector::BOP::divide'},
  219   '/ ' => {class => 'LimitedVector::BOP::divide'},
  220   'u+' => {class => 'LimitedVector::UOP::plus'},
  221   'u-' => {class => 'LimitedVector::UOP::minus'},
  222 );
  223 #
  224 #  Remove these operators and functions
  225 #
  226 $context{LimitedVector}->operators->undefine('_','U','><','.');
  227 $context{LimitedVector}->functions->undefine('norm','unit');
  228 $context{LimitedVector}->lists->set(
  229   AbsoluteValue => {class => 'LimitedVector::List::AbsoluteValue'},
  230   Vector        => {class => 'LimitedVector::List::Vector'},
  231 );
  232 #
  233 #  Format can be 'coordinate', 'ijk', or 'either'
  234 #
  235 $context{LimitedVector}->flags->set(vector_format => 'either');
  236 
  237 $context{'LimitedVector-ijk'} = $context{LimitedVector}->copy;
  238 $context{'LimitedVector-ijk'}->flags->set(vector_format => 'ijk');
  239 
  240 $context{'LimitedVector-coordinate'} = $context{LimitedVector}->copy;
  241 $context{'LimitedVector-coordinate'}->flags->set(vector_format => 'coordinate');
  242 $context{'LimitedVector-coordinate'}->constants->undefine('i','j','k');
  243 
  244 Context("LimitedVector");

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9