From 6cb3ed4ded6f5a5937301d0d9f61e732ed71e7e2 Mon Sep 17 00:00:00 2001 From: Peter Staab Date: Fri, 12 Jan 2024 07:56:31 -0500 Subject: [PATCH] Implement inserting variables in PGML answer blanks for some cases * convert AnswerFormatHelp to helpLink * remove AnswerFormatHelp from macro list * better handling of HR * better handling of code blocks inside of PGML. * when wrapping variables in [], handle arrays, hashes better. --- lib/WeBWorK/PG/ConvertToPGML.pm | 141 ++++++++++++++++++++++++++------ 1 file changed, 114 insertions(+), 27 deletions(-) diff --git a/lib/WeBWorK/PG/ConvertToPGML.pm b/lib/WeBWorK/PG/ConvertToPGML.pm index 38cfb496d0..423c8f2626 100644 --- a/lib/WeBWorK/PG/ConvertToPGML.pm +++ b/lib/WeBWorK/PG/ConvertToPGML.pm @@ -21,6 +21,21 @@ WeBWorK::PG::ConvertToPGML Converts a pg file to PGML format. +This script does a number of conversions: + +=over +=item Update the loadMacros call to include PGML.pl, eliminate MathObject.pl (since it is loaded by PGML.pl) +and adds PGcourse.pl to the end of the list. +=item Coverts BEGIN_TEXT/END_TEXT (and older versions of this), BEGIN_SOLUTION/END_SOLUTION, BEGIN_HINT/END_HINT +to their newer BEGIN_PGML blocks. +=item Convert math mode in these blocks to PGML style math mode. +=item Convert other styling (bold, italics) to PGML style. +=item Convert variables to the interpolated [$var] PGML style. +=item Convert some of the answer rules to newer PGML style. +=item Remove some outdated code. +=item A few other minor things. +=back + =head1 OPTIONS =cut @@ -43,8 +58,15 @@ our @EXPORT = qw(convertToPGML); # input is a string containing the source of the pg file to be converted. # returns a string that is the converted input string. +# This stores the answers inside of ANS and related functions. +my @ans_list; + sub convertToPGML { my ($pg_source) = @_; + + # First get a list of all of the ANS, LABELED_ANS, etc. in the problem. + @ans_list = getANS($pg_source); + my @pgml_block; my $in_pgml_block = 0; my @all_lines; @@ -53,10 +75,13 @@ sub convertToPGML { while (@rows) { my $row = shift @rows; - if ($row =~ /BEGIN_(TEXT|HINT|SOLUTION)/ || $row =~ /SOLUTION\(EV3\(<<\'END_SOLUTION\'\)\);/) { + if ($row =~ /BEGIN_(TEXT|HINT|SOLUTION)/ + || $row =~ /SOLUTION\(EV3\(<<\'END_SOLUTION\'\)\);/ + || $row =~ /TEXT\(EV2\(<)?\{.*?\})?)/[$1]/; + } + } - # if there is an $HR, add blank lines before and after the PGML "---" - if ($row =~ /\$HR/) { - push @new_rows, '', '---', ''; - } elsif ($add_blank_line_before) { + # Do some converting inside a perl block: + for (0 .. $#perl_block) { + $perl_block[$_] =~ s/AnswerFormatHelp\(["']([\w\s]+)["']\)/helpLink('$1')/g; + } + + if ($add_blank_line_before) { push @new_rows, '', $row; } elsif ($add_blank_line_after) { push @new_rows, $row, ''; + } elsif ($row =~ /^(.*)?\sPERL_BLOCK\s(.*)?$/) { + # remove any empty lines in the block + @perl_block = grep { $_ !~ /^\s*$/ } @perl_block; + # Wrap the perl block in [@ @] + if ($#perl_block == 0) { + push(@new_rows, ($1 // '') . ' [@ ' . $perl_block[0] . ' @]*' . ($2 // '')); + } else { + push(@new_rows, ($1 // '') . ' [@ ' . shift(@perl_block), @perl_block, ' @]*' . ($2 // '')); + } } else { push @new_rows, $row; } + } return \@new_rows; } -# Convert the ans_rule constructs to [_]{$var}. This is called recursively to handle multiple ans_rule -# on a single line. +# Convert many ans_rule constructs to the PGML answer blank form [_]{$var}. +# This is called recursively to handle multiple ans_rule on a single line. sub convertANSrule { my ($str) = @_; - if ($str =~ /(.*)\\\{\s*((\$\w+)->)?ans_rule(\((\d+)\))?\s*\\\}(.*)$/) { - my $var = $3 // ''; + if ($str =~ /(.*)\\\{\s*((\$\w+)->)?ans_rule(\((\d*)\))?\s*\\\}(.*)$/) { + my $ans = shift(@ans_list); + my $var = $3 // $ans->{arg} // ''; my $size = $5 ? "{$5}" : ''; return convertANSrule($1 // '') . '[_]' . "{$var}$size" . convertANSrule($6 // ''); } else { @@ -207,4 +269,29 @@ sub cleanUpCode { return $row; } +# Loads the entire file searching for instances of ANS, WEIGHTED_ANS, NAMED_ANS or LABELED_ANS +# and returns an arrayref with an ordered list of them. +sub getANS { + my ($pg_source) = @_; + my @ans_list; + for my $row (split(/\n/, $pg_source)) { + if ($row !~ /^\s*#/ && $row =~ /(LABELED_|NAMED_|WEIGHTED_|)ANS/) { + # For style like ANS($ans->cmp()); + if ($row =~ /((LABELED_|NAMED_|WEIGHTED_|)ANS)\(\s*([\$\w]+)->(\w+)(\(\))?\s*\)/) { + push(@ans_list, { type => $1, arg => $3 }); + # for style like ANS(num_cmp($ans)) + } elsif ($row =~ /((LABELED_|NAMED_|WEIGHTED_|)ANS)\(\s*(([\w\_]+)\((\$[\w\_]+)\))\)/) { + my $type = $1; + my $arg = $3 =~ s/(std_)?num_cmp/Real/r; + $arg =~ s/str_cmp|std_num_cmp/String/; + $arg =~ s/interval_cmp/Interval/; + $arg =~ s/fun_cmp/Formula/; + $arg =~ s/radio_cmp|checkbox_cmp//; + push(@ans_list, { type => $type, arg => $arg }); + } + } + } + return @ans_list; +} + 1;