[system] / trunk / pg / macros / contextLimitedVector.pl Repository: Repository Listing bbplugincoursesdistsnplrochestersystemwww

# View of /trunk/pg/macros/contextLimitedVector.pl

Tue Aug 28 22:40:15 2007 UTC (12 years, 5 months ago) by dpvc
File size: 8176 byte(s)
```Add context names for the context(s) created here.
```

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