[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 2726 - (download) (as text) (annotate)
Sat Sep 4 20:21:32 2004 UTC (15 years, 5 months ago) by dpvc
File size: 7819 byte(s)
These files provide contexts for the Parser in which only limited
operations are allowed.  The LimitedNumeric context is analogous to
strict_num_cmp().  The other contexts are similar, but for the
indicated type of answer.  In the LimitedVector context, for example,
the student can enter vectors, and can perform numeric operations
within the coordinates of the vectors, but can't perform vector
operations like vector addition or cross product.

    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, '$bop' can only be used with Numbers")
   51     if $self->{equation}{context}{flags}{vector_format} eq 'coordinate';
   52   $self->Error("In this context, '$bop' can only be used with Numbers or i,j and k");
   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 '$duplicate' may appear only once in your formula")
   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, '$bop' can only be used with Numbers");
  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, '$uop' can only be used with Numbers")
  150     if $self->{equation}{context}{flags}{vector_format} eq 'coordinate';
  151   $self->Error("In this context, '$uop' can only be used with Numbers or i,j and k");
  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