[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 1253 - (download) (as text) (annotate)
Mon Jun 23 18:34:44 2003 UTC (16 years, 8 months ago) by gage
File size: 14904 byte(s)
More changes to make these files compatible
with WeBWorK2.0 and cached compartments
-- Mike

    1 #!/usr/local/bin/webwork-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 #   DOCUMENT must come early in every .pg file, before any answers or text are
   81 # defined.  It initializes the variables.
   82 # It can appear only once.
   83 
   84 =head2 DOCUMENT()
   85 
   86 C<DOCUMENT()> must be the first statement in each problem template.  It can
   87 only be used once in each problem.
   88 
   89 C<DOCUMENT()> initializes some empty variables and via C<INITIALIZE_PG()> unpacks the
   90 variables in the C<%envir> variable which is implicitly passed to the problem. It must
   91 be the first statement in any problem template. It
   92 also unpacks any answers submitted and places them in the C<@submittedAnswer> list,
   93 saves the problem seed in C<$PG_original_problemSeed> in case you need it later, and
   94 initializes the pseudo random number generator object in C<$PG_random_generator>.
   95 
   96 You can reset the standard number generator using the command:
   97 
   98   $PG_random_generator->srand($new_seed_value);
   99 
  100 (See also C<SRAND> in the L<PGbasicmacros.pl> file.)
  101 
  102 The
  103 environment variable contents is defined in
  104 L</webwork_system_html/docs/techdescription/pglanguage/PGenvironment.html>
  105 
  106 
  107 =cut
  108 
  109 sub DOCUMENT {
  110 
  111   $STRINGforOUTPUT ="";
  112     $STRINGforHEADER_TEXT ="";
  113   @PG_ANSWERS=();
  114 
  115   @PG_UNLABELED_ANSWERS = ();
  116   %PG_ANSWERS_HASH = ();
  117   # FIXME:  We are initializing these variables into both Safe::Root1 (the cached safe compartment)
  118   # and Safe::Root2 (the current one)
  119   # There is a good chance they won't be properly updated in one or the other of these compartments.
  120 
  121   @main::PG_ANSWER_ENTRY_ORDER = ();
  122   $main::ANSWER_PREFIX = 'AnSwEr';
  123   %main::PG_FLAGS=();  #global flags
  124   $main::showPartialCorrectAnswers = 0 unless defined($main::showPartialCorrectAnswers );
  125   $main::showHint = 1 unless defined($main::showHint);
  126   $main::solutionExists =0;
  127   $main::hintExists =0;
  128   %main::gifs_created = ();
  129   eval(q!
  130   @main::PG_ANSWER_ENTRY_ORDER = ();
  131   $main::ANSWER_PREFIX = 'AnSwEr';
  132   %main::PG_FLAGS=();  #global flags
  133   $main::showPartialCorrectAnswers = 0 unless defined($main::showPartialCorrectAnswers );
  134   $main::showHint = 1 unless defined($main::showHint);
  135   $main::solutionExists =0;
  136   $main::hintExists =0;
  137   %main::gifs_created = ();
  138     !);
  139   die "The environment variable envir has not been defined" unless defined(%main::envir);
  140 
  141     foreach my $var ( keys %main::envir ) {
  142        eval("\$main::$var =\$main::envir{'$var'}");
  143        warn "Problem defining ", q{\$main::$var}, " while inititializing the PG problem: $@" if $@;
  144     }
  145     eval(q!
  146   @main::submittedAnswers = @{$main::refSubmittedAnswers} if defined($main::refSubmittedAnswers);
  147   $main::PG_original_problemSeed = $main::problemSeed;
  148   $main::PG_random_generator = new PGrandom($main::problemSeed) || die "Can't create random number generator.";
  149   $main::ans_rule_count = 0;  # counts questions
  150 
  151     # end unpacking of environment variables.
  152     $main::QUIZ_PREFIX = '' unless defined($main::QUIZ_PREFIX)
  153 
  154   !);
  155   @main::submittedAnswers = @{$main::refSubmittedAnswers} if defined($main::refSubmittedAnswers);
  156   $main::PG_original_problemSeed = $main::problemSeed;
  157   $main::PG_random_generator = new PGrandom($main::problemSeed) || die "Can't create random number generator.";
  158   $main::ans_rule_count = 0;  # counts questions
  159 
  160     # end unpacking of environment variables.
  161     $main::QUIZ_PREFIX = '' unless defined($main::QUIZ_PREFIX)
  162 
  163 }
  164 
  165 # HEADER_TEXT is for material which is destined to be placed in the header of the html problem -- such
  166 #   as javaScript code.
  167 
  168 =head2 HEADER_TEXT()
  169 
  170 
  171   HEADER_TEXT("string1", "string2", "string3");
  172 
  173 The C<HEADER_TEXT()>
  174 function concatenates its arguments and places them in the output
  175 header text string.  It is used for material which is destined to be placed in
  176 the header of the html problem -- such as javaScript code.
  177  It can be used more than once in a file.
  178 
  179 
  180 =cut
  181 
  182 sub HEADER_TEXT {
  183   my @in = @_;
  184   $STRINGforHEADER_TEXT .= join(" ",@in);
  185   }
  186 
  187 # TEXT is the function which defines text which will appear in the problem.
  188 # All text must be an argument to this function.  Any other statements
  189 #   are calculations (done in perl) which will not directly appear in the
  190 # output.  Think of this as the "print" function for the .pg language.
  191 # It can be used more than once in a file.
  192 
  193 =head2 TEXT()
  194 
  195   TEXT("string1", "string2", "string3");
  196 
  197 The C<TEXT()> function concatenates its arguments and places them in the output
  198 text string. C<TEXT()> is the function which defines text which will appear in the problem.
  199 All text must be an argument to this function.  Any other statements
  200 are calculations (done in perl) which will not directly appear in the
  201 output.  Think of this as the "print" function for the .pg language.
  202 It can be used more than once in a file.
  203 
  204 =cut
  205 
  206 sub TEXT {
  207   my @in = @_;
  208   $STRINGforOUTPUT .= join(" ",@in);
  209   }
  210 
  211 
  212 
  213 =head2 ANS()
  214 
  215   ANS(answer_evaluator1, answer_evaluator2, answer_evaluator3,...)
  216 
  217 Places the answer evaluators in the unlabeled answer_evaluator queue.  They will be paired
  218 with unlabeled answer rules (answer entry blanks) in the order entered.  This is the standard
  219 method for entering answers.
  220 
  221   LABELED_ANS(answer_evaluater_name1, answer_evaluator1, answer_evaluater_name2,answer_evaluator2,...)
  222 
  223 Places the answer evaluators in the labeled answer_evaluator hash.  This allows pairing of
  224 labeled answer evaluators and labeled answer rules which may not have been entered in the same
  225 order.
  226 
  227 =cut
  228 
  229 sub ANS{             # store answer evaluators which have not been explicitly labeled
  230   my @in = @_;
  231   while (@in ) {
  232          warn("<BR><B>Error in ANS:$in[0]</B> -- inputs must be references to
  233                       subroutines<BR>")
  234       unless ref($in[0]);
  235       push(@PG_ANSWERS, shift @in );
  236       }
  237 }
  238 sub LABELED_ANS {  #a better alias for NAMED_ANS
  239   &NAMED_ANS;
  240 }
  241 
  242 sub NAMED_ANS{     # store answer evaluators which have been explicitly labeled (submitted in a hash)
  243   my @in = @_;
  244   while (@in ) {
  245     my $label = shift @in;
  246     $label = $main::QUIZ_PREFIX.$label;
  247     my $ans_eval = shift @in;
  248     TEXT("<BR><B>Error in NAMED_ANS:$in[0]</B>
  249           -- inputs must be references to subroutines<BR>")
  250       unless ref($ans_eval);
  251     $PG_ANSWERS_HASH{$label}= $ans_eval;
  252   }
  253 }
  254 sub RECORD_ANS_NAME {     # this maintains the order in which the answer rules are printed.
  255   my $label = shift;
  256   push(@main::PG_ANSWER_ENTRY_ORDER, $label);
  257   $label;
  258 }
  259 
  260 sub NEW_ANS_NAME {        # this keeps track of the answers which are entered implicitly,
  261                           # rather than with a specific label
  262     my $number=shift;
  263     my $label = "$main::QUIZ_PREFIX$main::ANSWER_PREFIX$number";
  264     push(@PG_UNLABELED_ANSWERS,$label);
  265     $label;
  266 }
  267 sub ANS_NUM_TO_NAME {     # This converts a number to an answer label for use in
  268                           # radio button and check box answers. No new answer
  269                           # name is recorded.
  270     my $number=shift;
  271     my $label = "$main::QUIZ_PREFIX$main::ANSWER_PREFIX$number";
  272     $label;
  273 }
  274 
  275 my $vecnum;
  276 
  277 sub NEW_ANS_ARRAY_NAME {        # this keeps track of the answers which are entered implicitly,
  278                           # rather than with a specific label
  279     my $number=shift;
  280     $vecnum = 0;
  281     my $row = shift;
  282     my $col = shift;
  283     my $label = "ArRaY"."$number"."["."$vecnum".","."$row".","."$col"."]";
  284     push(@PG_UNLABELED_ANSWERS,$label);
  285     $label;
  286 }
  287 
  288 sub NEW_ANS_ARRAY_NAME_EXTENSION {        # this keeps track of the answers which are entered implicitly,
  289                           # rather than with a specific label
  290     my $number=shift;
  291     my $row = shift;
  292     my $col = shift;
  293     if( $row == 0 && $col == 0 ){
  294       $vecnum += 1;
  295     }
  296     my $label = "ArRaY"."$number"."["."$vecnum".","."$row".","."$col"."]";
  297     $label;
  298 }
  299 
  300 # ENDDOCUMENT must come at the end of every .pg file.
  301 #   It exports the resulting text of the problem, the text to be used in HTML header material
  302 #   (for javaScript), the list of answer evaluators and any other flags.  It can appear only once and
  303 #   it MUST be the last statement in the problem.
  304 
  305 =head2 ENDDOCUMENT()
  306 
  307 ENDDOCUMENT() must the last executable statement in any problem template.  It can
  308 only appear once.  It returns
  309 an array consisting of
  310 
  311   A reference to a string containing the rendered text of the problem.
  312   A reference to a string containing text to be placed in the header
  313                (for javaScript)
  314   A reference to the array containing the answer evaluators.
  315                (May be changed to a hash soon.)
  316   A reference to an associative array (hash) containing various flags.
  317 
  318   The following flags are set by ENDDOCUMENT:
  319   (1) showPartialCorrectAnswers  -- determines whether students are told which
  320       of their answers in a problem are wrong.
  321   (2) recordSubmittedAnswers  -- determines whether students submitted answers
  322       are saved.
  323   (3) refreshCachedImages  -- determines whether the cached image of the problem
  324       in typeset mode is always refreshed (i.e. setting this to 1 means cached
  325       images are not used).
  326   (4) solutionExits   -- indicates the existence of a solution.
  327   (5) hintExits   -- indicates the existence of a hint.
  328   (6) showHintLimit -- determines the number of attempts after which hint(s) will be shown
  329 
  330   (7) PROBLEM_GRADER_TO_USE -- chooses the problem grader to be used in this order
  331     (a) A problem grader specified by the problem using:
  332         install_problem_grader(\&grader);
  333     (b) One of the standard problem graders defined in PGanswermacros.pl when set to
  334         'std_problem_grader' or 'avg_problem_grader' by the environment variable
  335         $PG_environment{PROBLEM_GRADER_TO_USE}
  336     (c) A subroutine referenced by $PG_environment{PROBLEM_GRADER_TO_USE}
  337     (d) The default &std_problem_grader defined in PGanswermacros.pl
  338 
  339 
  340 =cut
  341 
  342 sub ENDDOCUMENT {
  343 
  344     my $index=0;
  345     foreach my $label (@PG_UNLABELED_ANSWERS) {
  346         if ( defined($PG_ANSWERS[$index]) ) {
  347         $PG_ANSWERS_HASH{"$label"}= $PG_ANSWERS[$index];
  348       } else {
  349         warn "No answer provided by instructor for answer $label";
  350       }
  351       $index++;
  352     }
  353 
  354     $STRINGforOUTPUT .="\n";
  355    ##eval q{  #make sure that "main" points to the current safe compartment by evaluating these lines.
  356     $main::PG_FLAGS{'showPartialCorrectAnswers'} = $main::showPartialCorrectAnswers;
  357     $main::PG_FLAGS{'recordSubmittedAnswers'} = $main::recordSubmittedAnswers;
  358     $main::PG_FLAGS{'refreshCachedImages'} = $main::refreshCachedImages;
  359     $main::PG_FLAGS{'hintExists'} = $main::hintExists;
  360     $main::PG_FLAGS{'showHintLimit'} = $main::showHint;
  361     $main::PG_FLAGS{'solutionExists'} = $main::solutionExists;
  362     $main::PG_FLAGS{ANSWER_ENTRY_ORDER} = \@main::PG_ANSWER_ENTRY_ORDER;
  363     $main::PG_FLAGS{ANSWER_PREFIX} = $main::ANSWER_PREFIX;
  364     # install problem grader
  365     if (defined($main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) ) {
  366       # problem grader defined within problem -- no further action needed
  367     } elsif ( defined( $main::envir{PROBLEM_GRADER_TO_USE} ) ) {
  368       if (ref($main::envir{PROBLEM_GRADER_TO_USE}) eq 'CODE' ) {         # user defined grader
  369         $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = $main::envir{PROBLEM_GRADER_TO_USE};
  370       } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'std_problem_grader' ) {
  371         if (defined(&std_problem_grader) ){
  372           $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl
  373         } # std_problem_grader is the default in any case so don't give a warning.
  374       } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'avg_problem_grader' ) {
  375         if (defined(&avg_problem_grader) ){
  376           $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&avg_problem_grader; # defined in PGanswermacros.pl
  377         }
  378         #else { # avg_problem_grader will be installed by PGtranslator so there is no need for a warning.
  379         # warn "The problem grader 'avg_problem_grader' has not been defined.  Has PGanswermacros.pl been loaded?";
  380         #}
  381       } else {
  382         warn "Error:  $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} is not a known program grader.";
  383       }
  384     } elsif (defined(&std_problem_grader)) {
  385       $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl
  386     } else {
  387       # PGtranslator will install its default problem grader
  388     }
  389   ##};
  390     warn "ERROR: The problem grader is not a subroutine" unless ref( $main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) eq 'CODE'
  391                      or $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = 'std_problem_grader'
  392                      or $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = 'avg_problem_grader';
  393      # return results
  394   (\$STRINGforOUTPUT, \$STRINGforHEADER_TEXT,\%PG_ANSWERS_HASH,\%main::PG_FLAGS);
  395 }
  396 
  397 
  398 
  399 =head2 INITIALIZE_PG()
  400 
  401 This is executed each C<DOCUMENT()> is called.  For backward compatibility
  402 C<loadMacros> also checks whether the C<macroDirectory> has been defined
  403 and if not, it runs C<INITIALIZE_PG()> and issues a warning.
  404 
  405 =cut
  406 
  407 
  408 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9