[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 6058 - (download) (as text) (annotate)
Thu Jun 25 23:28:44 2009 UTC (10 years, 7 months ago) by gage
File size: 8980 byte(s)
syncing pg HEAD with pg2.4.7 on 6/25/2009

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9