Parent Directory
|
Revision Log
dev-1-7-01
1 #!/usr/local/bin/perl 2 3 # This file provided the fundamental macros for the pg language 4 # These macros define the interface between the problems written by 5 # the professor and the processing which occurs in the script 6 # processProblem.pl 7 8 9 BEGIN { 10 be_strict(); 11 } 12 13 14 =head1 NAME 15 16 PG.pl --- located in the courseScripts directory. 17 Defines the Program Generating language at the most basic level. 18 19 =head1 SYNPOSIS 20 21 The basic PG problem structure: 22 23 DOCUMENT(); # should be the first statment in the problem 24 loadMacros(.....); # (optional) load other macro files if needed. 25 # (loadMacros is defined in F<dangerousMacros.pl>) 26 27 HEADER_TEXT(...); # (optional) used only for inserting javaScript into problems. 28 29 # # insert text of problems 30 TEXT("Problem text to be", 31 "displayed. Enter 1 in this blank:", 32 ANS_RULE(1,30) # ANS_RULE() defines an answer blank 30 characters long. 33 # It is defined in F<PGbasicmacros.pl> 34 ); 35 36 37 ANS( answer_evalutors); # see F<PGanswermacros.pl> for examples of answer evaluatiors. 38 39 ENDDOCUMENT() # must be the last statement in the problem 40 41 42 43 =head1 DESCRIPTION 44 45 As described in the synopsis, this file and the macros C<DOCUMENT()> and C<ENDDOCUMENT()> determine 46 the interface between problems written in the PG language and the rest of B<WeBWorK>, in particular 47 the subroutine C<createPGtext(()> in the file F<translate.pl>. 48 49 C<DOCUMENT()> must be the first statement in each problem template. 50 It initializes variables, 51 in particular all of the contents of the 52 environment variable become defined in the problem enviroment. 53 (See 54 L</webwork_system_html/docs/techdescription/pglanguage/PGenvironment.html>) 55 56 ENDDOCUMENT() must the last executable statement in any problem template. It returns 57 the rendered problem, answer evaluators and other flags to the rest of B<WeBWorK>, specificially 58 to the routine C<createPGtext()> defined in F<translate.pl> 59 60 61 The C<HEADER_TEXT()>, C<TEXT()>, and C<ANS()> functions load the 62 header text string, the problem text string. 63 and the answer evaulator queue respectively. 64 65 66 =cut 67 68 69 # Private variables for the PG.pl file. 70 71 my ($STRINGforOUTPUT, $STRINGforHEADER_TEXT, @PG_ANSWERS, @PG_ANSWER_ENTRY_ORDER,@PG_UNLABELED_ANSWERS); 72 my %PG_ANSWERS_HASH ; 73 74 # DOCUMENT must come early in every .pg file, before any answers or text are 75 # defined. It initializes the variables. 76 # It can appear only once. 77 78 =head2 DOCUMENT() 79 80 C<DOCUMENT()> must be the first statement in each problem template. It can 81 only be used once in each problem. 82 83 C<DOCUMENT()> initializes some empty variables and via C<INITIALIZE_PG()> unpacks the 84 variables in the C<%envir> variable which is implicitly passed to the problem. It must 85 be the first statement in any problem template. It 86 also unpacks any answers submitted and places them in the C<@submittedAnswer> list, 87 saves the problem seed in C<$PG_original_problemSeed> in case you need it later, and 88 initializes the pseudo random number generator object in C<$PG_random_generator>. 89 90 You can reset the standard number generator using the command: 91 92 $PG_random_generator->srand($new_seed_value); 93 94 (See also C<SRAND> in the L<PGbasicmacros.pl> file.) 95 96 The 97 environment variable contents is defined in 98 L</webwork_system_html/docs/techdescription/pglanguage/PGenvironment.html> 99 100 101 =cut 102 103 sub DOCUMENT { 104 $STRINGforOUTPUT =""; 105 $STRINGforHEADER_TEXT =""; 106 @PG_ANSWERS=(); 107 @PG_ANSWER_ENTRY_ORDER = (); 108 @PG_UNLABELED_ANSWERS = (); 109 %PG_ANSWERS_HASH = (); 110 $main::ANSWER_PREFIX = 'AnSwEr'; 111 %main::PG_FLAGS=(); #global flags 112 $main::showPartialCorrectAnswers = 0 unless defined($main::showPartialCorrectAnswers ); 113 $main::solutionExists =0; 114 %main::gifs_created = (); 115 116 die "The environment variable envir has not been defined" unless defined(%main::envir); 117 118 foreach my $var ( keys %main::envir ) { 119 eval("\$main::$var =\$main::envir{'$var'}"); 120 warn "Problem defining \$$main::var while inititializing the PG problem: $@" if $@; 121 } 122 123 @main::submittedAnswers = @{$main::refSubmittedAnswers} if defined($main::refSubmittedAnswers); 124 $main::PG_original_problemSeed = $main::problemSeed; 125 $main::PG_random_generator = new PGrandom($main::problemSeed) || die "Can't create random number generator."; 126 $main::ans_rule_count = 0; # counts questions 127 128 # end unpacking of environment variables. 129 } 130 131 # HEADER_TEXT is for material which is destined to be placed in the header of the html problem -- such 132 # as javaScript code. 133 134 =head2 HEADER_TEXT() 135 136 137 HEADER_TEXT("string1", "string2", "string3"); 138 139 The C<HEADER_TEXT()> 140 function concatenates its arguments and places them in the output 141 header text string. It is used for material which is destined to be placed in 142 the header of the html problem -- such as javaScript code. 143 It can be used more than once in a file. 144 145 146 =cut 147 148 sub HEADER_TEXT { 149 my @in = @_; 150 $STRINGforHEADER_TEXT .= join(" ",@in); 151 } 152 153 # TEXT is the function which defines text which will appear in the problem. 154 # All text must be an argument to this function. Any other statements 155 # are calculations (done in perl) which will not directly appear in the 156 # output. Think of this as the "print" function for the .pg language. 157 # It can be used more than once in a file. 158 159 =head2 TEXT() 160 161 TEXT("string1", "string2", "string3"); 162 163 The C<TEXT()> function concatenates its arguments and places them in the output 164 text string. C<TEXT()> is the function which defines text which will appear in the problem. 165 All text must be an argument to this function. Any other statements 166 are calculations (done in perl) which will not directly appear in the 167 output. Think of this as the "print" function for the .pg language. 168 It can be used more than once in a file. 169 170 =cut 171 172 sub TEXT { 173 my @in = @_; 174 $STRINGforOUTPUT .= join(" ",@in); 175 } 176 177 178 179 =head2 ANS() 180 181 ANS(answer_evaluator1, answer_evaluator2, answer_evaluator3,...) 182 183 Places the answer evaluators in the unlabeled answer_evaluator queue. They will be paired 184 with unlabeled answer rules (answer entry blanks) in the order entered. This is the standard 185 method for entering answers. 186 187 LABELED_ANS(answer_evaluater_name1, answer_evaluator1, answer_evaluater_name2,answer_evaluator2,...) 188 189 Places the answer evaluators in the labeled answer_evaluator hash. This allows pairing of 190 labeled answer evaluators and labeled answer rules which may not have been entered in the same 191 order. 192 193 =cut 194 195 sub ANS{ # store answer evaluators which have not been explicitly labeled 196 my @in = @_; 197 while (@in ) { 198 warn("<BR><B>Error in ANS:$in[0]</B> -- inputs must be references to 199 subroutines<BR>") 200 unless ref($in[0]); 201 push(@PG_ANSWERS, shift @in ); 202 } 203 } 204 205 sub NAMED_ANS{ # store answer evaluators which have been explicitly labeled (submitted in a hash) 206 my @in = @_; 207 while (@in ) { 208 my $label = shift @in; 209 my $ans_eval = shift @in; 210 TEXT("<BR><B>Error in NAMED_ANS:$in[0]</B> 211 -- inputs must be references to subroutines<BR>") 212 unless ref($ans_eval); 213 $PG_ANSWERS_HASH{$label}= $ans_eval; 214 } 215 } 216 sub RECORD_ANS_NAME { # this maintains the order in which the answer rules are printed. 217 my $label = shift; 218 push(@PG_ANSWER_ENTRY_ORDER, $label); 219 $label; 220 } 221 222 sub NEW_ANS_NAME { # this keeps track of the answers which are entered implicitly, 223 # rather than with a specific label 224 my $number=shift; 225 my $label = "$main::ANSWER_PREFIX$number"; 226 push(@PG_UNLABELED_ANSWERS,$label); 227 $label; 228 } 229 sub ANS_NUM_TO_NAME { # This converts a number to an answer label for use in 230 # radio button and check box answers. No new answer 231 # name is recorded. 232 my $number=shift; 233 my $label = "$main::ANSWER_PREFIX$number"; 234 $label; 235 } 236 237 # ENDDOCUMENT must come at the end of every .pg file. 238 # It exports the resulting text of the problem, the text to be used in HTML header material 239 # (for javaScript), the list of answer evaluators and any other flags. It can appear only once and 240 # it MUST be the last statement in the problem. 241 242 =head2 ENDDOCUMENT() 243 244 ENDDOCUMENT() must the last executable statement in any problem template. It can 245 only appear once. It returns 246 an array consisting of 247 248 A reference to a string containing the rendered text of the problem. 249 A reference to a string containing text to be placed in the header 250 (for javaScript) 251 A reference to the array containing the answer evaluators. 252 (May be changed to a hash soon.) 253 A reference to an associative array (hash) containing various flags. 254 255 The following flags are set by ENDDOCUMENT: 256 (1) showPartialCorrectAnswers -- determines whether students are told which 257 of their answers in a problem are wrong. 258 (2) recordSubmittedAnswers -- determines whether students submitted answers 259 are saved. 260 recordSubmittedAnswers 261 (3) solutionExits -- indicates the existence of a solution. 262 (4) PROBLEM_GRADER_TO_USE -- chooses the problem grader to be used in this order 263 (a) A problem grader specified by the problem using: 264 install_problem_grader(\&grader); 265 (b) One of the standard problem graders defined in PGanswermacros.pl when set to 266 'std_problem_grader' or 'avg_problem_grader' by the environment variable 267 $PG_environment{PROBLEM_GRADER_TO_USE} 268 (c) A subroutine referenced by $PG_environment{PROBLEM_GRADER_TO_USE} 269 (d) The default &std_problem_grader defined in PGanswermacros.pl 270 271 272 =cut 273 274 sub ENDDOCUMENT { 275 276 my $index=0; 277 foreach my $label (@PG_UNLABELED_ANSWERS) { 278 if ( defined($PG_ANSWERS[$index]) ) { 279 $PG_ANSWERS_HASH{"$label"}= $PG_ANSWERS[$index]; 280 } else { 281 warn "No answer provided by instructor for answer $label"; 282 } 283 $index++; 284 } 285 $STRINGforOUTPUT .="\n"; 286 $main::PG_FLAGS{'showPartialCorrectAnswers'} = $main::showPartialCorrectAnswers; 287 $main::PG_FLAGS{'recordSubmittedAnswers'} = $main::recordSubmittedAnswers; 288 # $main::PG_FLAGS{'hintExists'} = $main::hintExists; 289 $main::PG_FLAGS{'solutionExists'} = $main::solutionExists; 290 $main::PG_FLAGS{ANSWER_ENTRY_ORDER} = \@PG_ANSWER_ENTRY_ORDER; 291 $main::PG_FLAGS{ANSWER_PREFIX} = $main::ANSWER_PREFIX; 292 # install problem grader 293 if (defined($main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) ) { 294 # problem grader defined within problem -- no further action needed 295 } elsif ( defined( $main::envir{PROBLEM_GRADER_TO_USE} ) ) { 296 if (ref($main::envir{PROBLEM_GRADER_TO_USE}) eq 'CODE' ) { # user defined grader 297 $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = $main::envir{PROBLEM_GRADER_TO_USE}; 298 } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'std_problem_grader' ) { 299 if (defined(&std_problem_grader) ){ 300 $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl 301 } # std_problem_grader is the default in any case so don't give a warning. 302 } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'avg_problem_grader' ) { 303 if (defined(&avg_problem_grader) ){ 304 $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&avg_problem_grader; # defined in PGanswermacros.pl 305 } else { 306 warn "The problem grader 'avg_problem_grader' has not been defined. Has PGanswermacros.pl been loaded?"; 307 } 308 } else { 309 warn "Error: $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} is not a known program grader."; 310 } 311 } elsif (defined(&std_problem_grader)) { 312 $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl 313 } else { 314 # PGtranslator will install its default problem grader 315 } 316 warn "ERROR: The problem grader is not a subroutine" unless ref( $main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) eq 'CODE'; 317 318 # return results 319 (\$STRINGforOUTPUT, \$STRINGforHEADER_TEXT,\%PG_ANSWERS_HASH,\%main::PG_FLAGS); 320 } 321 322 323 324 =head2 INITIALIZE_PG() 325 326 This is executed each C<DOCUMENT()> is called. For backward compatibility 327 C<loadMacros> also checks whether the C<macroDirectory> has been defined 328 and if not, it runs C<INITIALIZE_PG()> and issues a warning. 329 330 =cut 331 332 ################################################################################ 333 # Initialize the global variables to be used in PG 334 ######### 335 336 #HACK -- fix this 337 unless (defined(&PGrandom::srand) ) { 338 339 do "$main::envir{courseScriptsDirectory}PGrandom.pm" || 340 die "Can't read $envir{courseScriptsDirectory}PGrandom.pm"; 341 } 342 # 343 344 345 ######### 346 # Initialization is complete 347 ################################################################################ 348 349 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |