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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9