Parent Directory
|
Revision Log
added standard copyright/license header
1 ################################################################################ 2 # WeBWorK Online Homework Delivery System 3 # Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ 4 # $CVSHeader: webwork2/lib/WeBWorK.pm,v 1.100 2007/08/13 22:59:53 sh002i Exp $ 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 |