Parent Directory
|
Revision Log
Revision 3371 - (view) (download) (as text)
| 1 : | dpvc | 2726 | 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 : | dpvc | 3371 | $self->Error("In this context, '%s' can only be used with Numbers",$bop) |
| 51 : | dpvc | 2726 | if $self->{equation}{context}{flags}{vector_format} eq 'coordinate'; |
| 52 : | dpvc | 3371 | $self->Error("In this context, '%s' can only be used with Numbers or i,j and k",$bop); |
| 53 : | dpvc | 2726 | } |
| 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 : | dpvc | 3371 | Value::Error("The constant '%s' may appear only once in your formula",$duplicate) |
| 78 : | dpvc | 2726 | 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 : | dpvc | 3371 | $self->Error("In this context, '%s' can only be used with Numbers",$bop); |
| 129 : | dpvc | 2726 | } |
| 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 : | dpvc | 3371 | $self->Error("In this context, '%s' can only be used with Numbers",$uop) |
| 150 : | dpvc | 2726 | if $self->{equation}{context}{flags}{vector_format} eq 'coordinate'; |
| 151 : | dpvc | 3371 | $self->Error("In this context, '%s' can only be used with Numbers or i,j and k",$uop); |
| 152 : | dpvc | 2726 | } |
| 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 |