Parent Directory
|
Revision Log
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 |