[system] / trunk / pg / macros / PG.pl Repository:
ViewVC logotype

View of /trunk/pg/macros/PG.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1365 - (download) (as text) (annotate)
Fri Jul 11 20:18:57 2003 UTC (16 years, 6 months ago) by lr003k
File size: 15999 byte(s)
In order to make ans_array entries "sticky" I've had to change some stuff.
The answer labels for these entries are now put into $main::PG_FLAGS{ANSWER_ENTRY_ORDER} as an array reference in PG.pl.
In processProblem8.pl the labels are caught again in the subroutines logCourseData and encode_submitted_answers. It is important that
they're caught there rather than earlier because then the translator would think that every entry was it's own answer.
Professor Gage says that processProblem8.pl is no longer used in webwork2, and he says he'll fix this there.

    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 sub _PG_init{
   14 
   15 }
   16 
   17 #package PG;
   18 
   19 
   20 =head1 NAME
   21 
   22   PG.pl --- located in the courseScripts directory.
   23   Defines the Program Generating language at the most basic level.
   24 
   25 =head1 SYNPOSIS
   26 
   27   The basic PG problem structure:
   28 
   29   DOCUMENT();          # should be the first statment in the problem
   30   loadMacros(.....);   # (optional) load other macro files if needed.
   31                        # (loadMacros is defined in F<dangerousMacros.pl>)
   32 
   33   HEADER_TEXT(...);    # (optional) used only for inserting javaScript into problems.
   34 
   35   #            #  insert text of problems
   36   TEXT("Problem text to be",
   37        "displayed. Enter 1 in this blank:",
   38        ANS_RULE(1,30)  #  ANS_RULE() defines an answer blank 30 characters long.
   39                        #  It is defined in F<PGbasicmacros.pl>
   40        );
   41 
   42 
   43   ANS( answer_evalutors);  # see F<PGanswermacros.pl> for examples of answer evaluatiors.
   44 
   45   ENDDOCUMENT()        # must be the last statement in the problem
   46 
   47 
   48 
   49 =head1 DESCRIPTION
   50 
   51 As described in the synopsis, this file and the macros C<DOCUMENT()> and C<ENDDOCUMENT()> determine
   52 the interface between problems written in the PG language and the rest of B<WeBWorK>, in particular
   53 the subroutine C<createPGtext(()> in the file F<translate.pl>.
   54 
   55 C<DOCUMENT()> must be the first statement in each problem template.
   56 It  initializes variables,
   57 in particular all of the contents of the
   58 environment variable  become defined in the problem enviroment.
   59 (See
   60 L</webwork_system_html/docs/techdescription/pglanguage/PGenvironment.html>)
   61 
   62 ENDDOCUMENT() must the last executable statement in any problem template.  It returns
   63 the rendered problem, answer evaluators and other flags to the rest of B<WeBWorK>, specificially
   64 to the routine C<createPGtext()> defined in F<translate.pl>
   65 
   66 
   67 The C<HEADER_TEXT()>, C<TEXT()>, and C<ANS()> functions load the
   68 header text string, the problem text string.
   69 and the answer evaulator queue respectively.
   70 
   71 
   72 =cut
   73 
   74 
   75 #  Private variables for the PG.pl file.
   76 
   77 my ($STRINGforOUTPUT, $STRINGforHEADER_TEXT, @PG_ANSWERS, @PG_UNLABELED_ANSWERS);
   78 my %PG_ANSWERS_HASH ;
   79 
   80 # my variables are unreliable if two DOCUMENTS were to be called before and ENDDOCUMENT
   81 # there could be conflicts.  As I understand the behavior of the Apache child
   82 # this cannot occur -- a child finishes with one request before obtaining the next
   83 
   84 #   DOCUMENT must come early in every .pg file, before any answers or text are
   85 # defined.  It initializes the variables.
   86 # It can appear only once.
   87 
   88 =head2 DOCUMENT()
   89 
   90 C<DOCUMENT()> must be the first statement in each problem template.  It can
   91 only be used once in each problem.
   92 
   93 C<DOCUMENT()> initializes some empty variables and via C<INITIALIZE_PG()> unpacks the
   94 variables in the C<%envir> variable which is implicitly passed to the problem. It must
   95 be the first statement in any problem template. It
   96 also unpacks any answers submitted and places them in the C<@submittedAnswer> list,
   97 saves the problem seed in C<$PG_original_problemSeed> in case you need it later, and
   98 initializes the pseudo random number generator object in C<$PG_random_generator>.
   99 
  100 You can reset the standard number generator using the command:
  101 
  102   $PG_random_generator->srand($new_seed_value);
  103 
  104 (See also C<SRAND> in the L<PGbasicmacros.pl> file.)
  105 
  106 The
  107 environment variable contents is defined in
  108 L</webwork_system_html/docs/techdescription/pglanguage/PGenvironment.html>
  109 
  110 
  111 =cut
  112 
  113 sub DOCUMENT {
  114 
  115   $STRINGforOUTPUT ="";
  116     $STRINGforHEADER_TEXT ="";
  117   @PG_ANSWERS=();
  118 
  119   @PG_UNLABELED_ANSWERS = ();
  120   %PG_ANSWERS_HASH = ();
  121   # FIXME:  We are initializing these variables into both Safe::Root1 (the cached safe compartment)
  122   # and Safe::Root2 (the current one)
  123   # There is a good chance they won't be properly updated in one or the other of these compartments.
  124 
  125 #   @main::PG_ANSWER_ENTRY_ORDER = ();
  126 #   $main::ANSWER_PREFIX = 'AnSwEr';
  127 #   %main::PG_FLAGS=();  #global flags
  128 #   $main::showPartialCorrectAnswers = 0 unless defined($main::showPartialCorrectAnswers );
  129 #   $main::showHint = 1 unless defined($main::showHint);
  130 #   $main::solutionExists =0;
  131 #   $main::hintExists =0;
  132 #   %main::gifs_created = ();
  133   eval(q!
  134   @main::PG_ANSWER_ENTRY_ORDER = ();
  135   $main::ANSWER_PREFIX = 'AnSwEr';
  136   %main::PG_FLAGS=();  #global flags
  137   $main::showPartialCorrectAnswers = 0 unless defined($main::showPartialCorrectAnswers );
  138   $main::showHint = 1 unless defined($main::showHint);
  139   $main::solutionExists =0;
  140   $main::hintExists =0;
  141   %main::gifs_created = ();
  142 
  143     !);
  144 #    warn eval(q! "PG.pl:  The envir variable $main::{envir} is".join(" ",%main::envir)!);
  145     my $rh_envir = eval(q!\%main::envir!);
  146     my %envir    = %$rh_envir;
  147     #no strict;
  148     foreach  my  $var (keys %envir) {
  149       eval(q!$main::!.$var.q! = $main::envir{!.$var.q!}! );  #whew!! makes sure $var is interpolated but $main:: is evaluated at run time.
  150     #    warn eval(q! "var $var is defined ". $main::!.$var);
  151         warn "Problem defining ", q{\$main::}.$var, " while initializing the PG problem: $@" if $@;
  152     }
  153     #use strict;
  154     #FIXME these strict pragmas don't seem to be needed and they cause trouble in perl 5.6.0
  155 
  156 
  157 
  158     eval(q!
  159   @main::submittedAnswers = @{$main::refSubmittedAnswers} if defined($main::refSubmittedAnswers);
  160   $main::PG_original_problemSeed = $main::problemSeed;
  161   $main::PG_random_generator = new PGrandom($main::problemSeed) || die "Can't create random number generator.";
  162   $main::ans_rule_count = 0;  # counts questions
  163 
  164     # end unpacking of environment variables.
  165     $main::QUIZ_PREFIX = '' unless defined($main::QUIZ_PREFIX)
  166 
  167   !);
  168 #   @main::submittedAnswers = @{$main::refSubmittedAnswers} if defined($main::refSubmittedAnswers);
  169 #   $main::PG_original_problemSeed = $main::problemSeed;
  170 #   $main::PG_random_generator = new PGrandom($main::problemSeed) || die "Can't create random number generator.";
  171 #   $main::ans_rule_count = 0;  # counts questions
  172 
  173     # end unpacking of environment variables.
  174 #   $main::QUIZ_PREFIX = '' unless defined($main::QUIZ_PREFIX)
  175 
  176 }
  177 
  178 sub inc_ans_rule_count {
  179   eval(q!++$main::ans_rule_count!); # evalute at runtime to get correct main::
  180 }
  181 # HEADER_TEXT is for material which is destined to be placed in the header of the html problem -- such
  182 #   as javaScript code.
  183 
  184 =head2 HEADER_TEXT()
  185 
  186 
  187   HEADER_TEXT("string1", "string2", "string3");
  188 
  189 The C<HEADER_TEXT()>
  190 function concatenates its arguments and places them in the output
  191 header text string.  It is used for material which is destined to be placed in
  192 the header of the html problem -- such as javaScript code.
  193  It can be used more than once in a file.
  194 
  195 
  196 =cut
  197 
  198 sub HEADER_TEXT {
  199   my @in = @_;
  200   $STRINGforHEADER_TEXT .= join(" ",@in);
  201   }
  202 
  203 # TEXT is the function which defines text which will appear in the problem.
  204 # All text must be an argument to this function.  Any other statements
  205 #   are calculations (done in perl) which will not directly appear in the
  206 # output.  Think of this as the "print" function for the .pg language.
  207 # It can be used more than once in a file.
  208 
  209 =head2 TEXT()
  210 
  211   TEXT("string1", "string2", "string3");
  212 
  213 The C<TEXT()> function concatenates its arguments and places them in the output
  214 text string. C<TEXT()> is the function which defines text which will appear in the problem.
  215 All text must be an argument to this function.  Any other statements
  216 are calculations (done in perl) which will not directly appear in the
  217 output.  Think of this as the "print" function for the .pg language.
  218 It can be used more than once in a file.
  219 
  220 =cut
  221 
  222 sub TEXT {
  223   my @in = @_;
  224   $STRINGforOUTPUT .= join(" ",@in);
  225   }
  226 
  227 
  228 
  229 =head2 ANS()
  230 
  231   ANS(answer_evaluator1, answer_evaluator2, answer_evaluator3,...)
  232 
  233 Places the answer evaluators in the unlabeled answer_evaluator queue.  They will be paired
  234 with unlabeled answer rules (answer entry blanks) in the order entered.  This is the standard
  235 method for entering answers.
  236 
  237   LABELED_ANS(answer_evaluater_name1, answer_evaluator1, answer_evaluater_name2,answer_evaluator2,...)
  238 
  239 Places the answer evaluators in the labeled answer_evaluator hash.  This allows pairing of
  240 labeled answer evaluators and labeled answer rules which may not have been entered in the same
  241 order.
  242 
  243 =cut
  244 
  245 sub ANS{             # store answer evaluators which have not been explicitly labeled
  246   my @in = @_;
  247   while (@in ) {
  248          warn("<BR><B>Error in ANS:$in[0]</B> -- inputs must be references to
  249                       subroutines<BR>")
  250       unless ref($in[0]);
  251       push(@PG_ANSWERS, shift @in );
  252       }
  253 }
  254 sub LABELED_ANS {  #a better alias for NAMED_ANS
  255   &NAMED_ANS;
  256 }
  257 
  258 sub NAMED_ANS{     # store answer evaluators which have been explicitly labeled (submitted in a hash)
  259   my @in = @_;
  260   while (@in ) {
  261     my $label = shift @in;
  262     $label = eval(q!$main::QUIZ_PREFIX.$label!);
  263     my $ans_eval = shift @in;
  264     TEXT("<BR><B>Error in NAMED_ANS:$in[0]</B>
  265           -- inputs must be references to subroutines<BR>")
  266       unless ref($ans_eval);
  267     $PG_ANSWERS_HASH{$label}= $ans_eval;
  268   }
  269 }
  270 sub RECORD_ANS_NAME {     # this maintains the order in which the answer rules are printed.
  271   my $label = shift;
  272   eval(q!push(@main::PG_ANSWER_ENTRY_ORDER, $label)!);
  273   $label;
  274 }
  275 
  276 sub NEW_ANS_NAME {        # this keeps track of the answers which are entered implicitly,
  277                           # rather than with a specific label
  278     my $number=shift;
  279     my $prefix = eval(q!$main::QUIZ_PREFIX.$main::ANSWER_PREFIX!);
  280     my $label = $prefix.$number;
  281     push(@PG_UNLABELED_ANSWERS,$label);
  282     $label;
  283 }
  284 sub ANS_NUM_TO_NAME {     # This converts a number to an answer label for use in
  285                           # radio button and check box answers. No new answer
  286                           # name is recorded.
  287     my $number=shift;
  288     my $label = eval(q!$main::QUIZ_PREFIX$main::ANSWER_PREFIX$number"!);
  289     $label;
  290 }
  291 
  292 my $vecnum;
  293 
  294 sub NEW_ANS_ARRAY_NAME {        # this keeps track of the answers which are entered implicitly,
  295                           # rather than with a specific label
  296     my $number=shift;
  297     $vecnum = 0;
  298     my $row = shift;
  299     my $col = shift;
  300     my $label = "ArRaY"."$number"."["."$vecnum".","."$row".","."$col"."]";
  301     push(@PG_UNLABELED_ANSWERS,$label);
  302     $label;
  303 }
  304 
  305 sub NEW_ANS_ARRAY_NAME_EXTENSION {        # this keeps track of the answers which are entered implicitly,
  306                           # rather than with a specific label
  307     my $number=shift;
  308     my $row = shift;
  309     my $col = shift;
  310     if( $row == 0 && $col == 0 ){
  311       $vecnum += 1;
  312     }
  313     my $label = "ArRaY"."$number"."["."$vecnum".","."$row".","."$col"."]";
  314     eval(q!push(@main::KEPT_EXTRA_ANSWERS, $label)!);#put the labels into the hash to be caught later for recording purposes
  315     $label;
  316 }
  317 
  318 # ENDDOCUMENT must come at the end of every .pg file.
  319 #   It exports the resulting text of the problem, the text to be used in HTML header material
  320 #   (for javaScript), the list of answer evaluators and any other flags.  It can appear only once and
  321 #   it MUST be the last statement in the problem.
  322 
  323 =head2 ENDDOCUMENT()
  324 
  325 ENDDOCUMENT() must the last executable statement in any problem template.  It can
  326 only appear once.  It returns
  327 an array consisting of
  328 
  329   A reference to a string containing the rendered text of the problem.
  330   A reference to a string containing text to be placed in the header
  331                (for javaScript)
  332   A reference to the array containing the answer evaluators.
  333                (May be changed to a hash soon.)
  334   A reference to an associative array (hash) containing various flags.
  335 
  336   The following flags are set by ENDDOCUMENT:
  337   (1) showPartialCorrectAnswers  -- determines whether students are told which
  338       of their answers in a problem are wrong.
  339   (2) recordSubmittedAnswers  -- determines whether students submitted answers
  340       are saved.
  341   (3) refreshCachedImages  -- determines whether the cached image of the problem
  342       in typeset mode is always refreshed (i.e. setting this to 1 means cached
  343       images are not used).
  344   (4) solutionExits   -- indicates the existence of a solution.
  345   (5) hintExits   -- indicates the existence of a hint.
  346   (6) showHintLimit -- determines the number of attempts after which hint(s) will be shown
  347 
  348   (7) PROBLEM_GRADER_TO_USE -- chooses the problem grader to be used in this order
  349     (a) A problem grader specified by the problem using:
  350         install_problem_grader(\&grader);
  351     (b) One of the standard problem graders defined in PGanswermacros.pl when set to
  352         'std_problem_grader' or 'avg_problem_grader' by the environment variable
  353         $PG_environment{PROBLEM_GRADER_TO_USE}
  354     (c) A subroutine referenced by $PG_environment{PROBLEM_GRADER_TO_USE}
  355     (d) The default &std_problem_grader defined in PGanswermacros.pl
  356 
  357 
  358 =cut
  359 
  360 sub ENDDOCUMENT {
  361 
  362     my $index=0;
  363     foreach my $label (@PG_UNLABELED_ANSWERS) {
  364         if ( defined($PG_ANSWERS[$index]) ) {
  365         $PG_ANSWERS_HASH{"$label"}= $PG_ANSWERS[$index];
  366       #warn "recording answer label = $label";
  367       } else {
  368         warn "No answer provided by instructor for answer $label";
  369       }
  370       $index++;
  371     }
  372 
  373     $STRINGforOUTPUT .="\n";
  374    eval q{  #make sure that "main" points to the current safe compartment by evaluating these lines.
  375     $main::PG_FLAGS{'showPartialCorrectAnswers'} = $main::showPartialCorrectAnswers;
  376     $main::PG_FLAGS{'recordSubmittedAnswers'} = $main::recordSubmittedAnswers;
  377     $main::PG_FLAGS{'refreshCachedImages'} = $main::refreshCachedImages;
  378     $main::PG_FLAGS{'hintExists'} = $main::hintExists;
  379     $main::PG_FLAGS{'showHintLimit'} = $main::showHint;
  380     $main::PG_FLAGS{'solutionExists'} = $main::solutionExists;
  381     $main::PG_FLAGS{ANSWER_ENTRY_ORDER} = \@main::PG_ANSWER_ENTRY_ORDER;
  382     $main::PG_FLAGS{KEPT_EXTRA_ANSWERS} = \@main::KEPT_EXTRA_ANSWERS;##need to keep array labels that don't call "RECORD_ANS_NAME"
  383     $main::PG_FLAGS{ANSWER_PREFIX} = $main::ANSWER_PREFIX;
  384     # install problem grader
  385     if (defined($main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) ) {
  386       # problem grader defined within problem -- no further action needed
  387     } elsif ( defined( $main::envir{PROBLEM_GRADER_TO_USE} ) ) {
  388       if (ref($main::envir{PROBLEM_GRADER_TO_USE}) eq 'CODE' ) {         # user defined grader
  389         $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = $main::envir{PROBLEM_GRADER_TO_USE};
  390       } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'std_problem_grader' ) {
  391         if (defined(&std_problem_grader) ){
  392           $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl
  393         } # std_problem_grader is the default in any case so don't give a warning.
  394       } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'avg_problem_grader' ) {
  395         if (defined(&avg_problem_grader) ){
  396           $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&avg_problem_grader; # defined in PGanswermacros.pl
  397         }
  398         #else { # avg_problem_grader will be installed by PGtranslator so there is no need for a warning.
  399         # warn "The problem grader 'avg_problem_grader' has not been defined.  Has PGanswermacros.pl been loaded?";
  400         #}
  401       } else {
  402         warn "Error:  $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} is not a known program grader.";
  403       }
  404     } elsif (defined(&std_problem_grader)) {
  405       $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl
  406     } else {
  407       # PGtranslator will install its default problem grader
  408     }
  409 
  410     warn "ERROR: The problem grader is not a subroutine" unless ref( $main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) eq 'CODE'
  411                      or $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = 'std_problem_grader'
  412                      or $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = 'avg_problem_grader';
  413      # return results
  414     };
  415 
  416   (\$STRINGforOUTPUT, \$STRINGforHEADER_TEXT,\%PG_ANSWERS_HASH,eval(q!\%main::PG_FLAGS!));
  417 }
  418 
  419 
  420 
  421 =head2 INITIALIZE_PG()
  422 
  423 This is executed each C<DOCUMENT()> is called.  For backward compatibility
  424 C<loadMacros> also checks whether the C<macroDirectory> has been defined
  425 and if not, it runs C<INITIALIZE_PG()> and issues a warning.
  426 
  427 =cut
  428 
  429 
  430 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9