From 8100cfa0f0efcaf9c1817384a4d822022d7ae6de Mon Sep 17 00:00:00 2001 From: Peter Staab Date: Thu, 28 May 2026 15:20:51 -0400 Subject: [PATCH 1/3] Add the ability to parse loadMacros if the arguments are in a qw block. Also, removes empty macros and duplicate macros. In addition, if it appears that the problem is already in PGML mode, then return the file without changes. --- lib/WeBWorK/PG/ConvertToPGML.pm | 67 ++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/lib/WeBWorK/PG/ConvertToPGML.pm b/lib/WeBWorK/PG/ConvertToPGML.pm index 04e4eeddba..b8f67c77e2 100644 --- a/lib/WeBWorK/PG/ConvertToPGML.pm +++ b/lib/WeBWorK/PG/ConvertToPGML.pm @@ -70,12 +70,21 @@ This returns a string that is the converted input string. =cut +use Mojo::Util qw(dumper); + # This stores the answers inside of ANS and related functions. my @ans_list; sub convertToPGML { my ($pg_source) = @_; + print dumper ($pg_source); + + # Check that the file is not already in PGML format by looking for PGML.pl in the loadMacros statement. + # and there are no BEGIN_TEXT, BEGIN_SOLUTION, etc. blocks. + + return $pg_source if ($pg_source =~ /loadMacros\((.*)PGML\.pl(.*)\)/ && $pg_source !~ /BEGIN_(TEXT|HINT|SOLUTION)/); + # First get a list of all of the ANS, LABELED_ANS, etc. in the problem. @ans_list = getANS($pg_source); @@ -101,29 +110,45 @@ sub convertToPGML { } elsif ($in_pgml_block) { push(@pgml_block, $row); } elsif ($row =~ /loadMacros\(/) { - # Parse the macros, which may be on multiple rows. - # Remove comments within loadMacros block (should we keep them?) - my $macros = $row; - while ($row && $row !~ /\);\s*$/) { + # Parse the macros, which may be on multiple rows and may be in a qw block. + my $macros = ''; + while (1) { + # Remove comments within loadMacros block (should we keep them?) + $row =~ s/#.*$//; + $macros .= $row; + last if ($row =~ /(.*)\);/); $row = shift @rows; - my @mrow = split(/#/, $row); - # This only adds the row if there is something relevent to the left of a # - $macros .= $mrow[0] if $mrow[0] !~ /^\s*$/; } - # Split by commas and pull out the quotes. - my @macros = - grep { $_ !~ /^#/ } - grep { - $_ !~ - /(PGstandard|PGML|PGauxiliaryFunctions|PGbasicmacros|PGanswermacros|MathObjects|PGcourse|AnswerFormatHelp).pl/ - } - map {s/['"\s]//gr} - split(/\s*,\s*/, $macros =~ s/loadMacros\((.*)\)\;$/$1/r); - - push(@all_lines, - 'loadMacros(' - . join(', ', map {"'$_'"} ('PGstandard.pl', 'PGML.pl', @macros, 'PGcourse.pl')) - . ');'); + + my @macros = (); + my ($qw_start, $qw_end); # the characters if the loadMacros has a qw block. + + # The following can parse loadMacros in the form loadMacros('macro1.pl', 'macro2.pl'); or + # loadMacros(qw{macro1.pl macro2.pl}); + if ($macros =~ /loadMacros\((qw(.))?(.*?)(.)?\)/ms) { + ($qw_start, $qw_end) = ($2, $4); + @macros = + grep { + $_ + && $_ !~ + /(PGstandard|PGML|PGauxiliaryFunctions|PGbasicmacros|PGanswermacros|MathObjects|PGcourse|AnswerFormatHelp).pl/ + } + map {s/['"]//gr} split(/\s+|\s*,\s*/, $3); + + # Remove any duplicates: + my %seen; + @macros = grep { !$seen{$_}++ } @macros; + } else { + warn 'The loadMacros statement in this file could not be processed.'; + } + + @macros = ('PGstandard.pl', 'PGML.pl', @macros, 'PGcourse.pl'); + + if ($qw_start) { + push(@all_lines, "loadMacros(qw$qw_start\n\t" . join("\n\t", @macros) . "\n$qw_end);"); + } else { + push(@all_lines, 'loadMacros(' . join(', ', map {"'$_'"} @macros) . ');'); + } } else { push(@all_lines, cleanUpCode($row)); } From 9d0c24430278faeafe6d87ffbea8e766d2a028ce Mon Sep 17 00:00:00 2001 From: Peter Staab Date: Mon, 1 Jun 2026 09:13:55 -0400 Subject: [PATCH 2/3] remove debug statements --- lib/WeBWorK/PG/ConvertToPGML.pm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/WeBWorK/PG/ConvertToPGML.pm b/lib/WeBWorK/PG/ConvertToPGML.pm index b8f67c77e2..31e5e0f5ee 100644 --- a/lib/WeBWorK/PG/ConvertToPGML.pm +++ b/lib/WeBWorK/PG/ConvertToPGML.pm @@ -70,16 +70,12 @@ This returns a string that is the converted input string. =cut -use Mojo::Util qw(dumper); - # This stores the answers inside of ANS and related functions. my @ans_list; sub convertToPGML { my ($pg_source) = @_; - print dumper ($pg_source); - # Check that the file is not already in PGML format by looking for PGML.pl in the loadMacros statement. # and there are no BEGIN_TEXT, BEGIN_SOLUTION, etc. blocks. From ba081b2d921c95d35c9729ba41cdf2e97ae47d32 Mon Sep 17 00:00:00 2001 From: Peter Staab Date: Tue, 9 Jun 2026 11:49:20 -0400 Subject: [PATCH 3/3] Handle cases where qw blocks are mixed with strings in ' or " in loadMacros --- lib/WeBWorK/PG/ConvertToPGML.pm | 34 +++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/WeBWorK/PG/ConvertToPGML.pm b/lib/WeBWorK/PG/ConvertToPGML.pm index 31e5e0f5ee..dfb9c5e863 100644 --- a/lib/WeBWorK/PG/ConvertToPGML.pm +++ b/lib/WeBWorK/PG/ConvertToPGML.pm @@ -107,29 +107,45 @@ sub convertToPGML { push(@pgml_block, $row); } elsif ($row =~ /loadMacros\(/) { # Parse the macros, which may be on multiple rows and may be in a qw block. - my $macros = ''; + my $macros = ''; + my $num_macro_lines = 0; # store the number of lines in the loadMacro so the output is similar to the input. while (1) { # Remove comments within loadMacros block (should we keep them?) $row =~ s/#.*$//; $macros .= $row; last if ($row =~ /(.*)\);/); + ++$num_macro_lines; $row = shift @rows; } - my @macros = (); my ($qw_start, $qw_end); # the characters if the loadMacros has a qw block. # The following can parse loadMacros in the form loadMacros('macro1.pl', 'macro2.pl'); or # loadMacros(qw{macro1.pl macro2.pl}); - if ($macros =~ /loadMacros\((qw(.))?(.*?)(.)?\)/ms) { - ($qw_start, $qw_end) = ($2, $4); + if ($macros =~ /loadMacros\((.*?)\);/ms) { + my @macro_str = split(/\s*,\s*/, $1); + + for my $str (@macro_str) { + if ($str =~ /^qw(.)/) { + my $qw_matches = { '{' => '}', '(' => ')', '[' => ']', '/' => '/', '|' => '|' }; + $qw_start = $1; + $qw_end = $qw_matches->{$qw_start}; + + if ($str =~ /^qw\Q${qw_start}\E(.*?)\Q${qw_end}\E/) { + push(@macros, split(/\s+/, $1)); + } + } else { + push(@macros, $str); + } + } + @macros = grep { $_ && $_ !~ /(PGstandard|PGML|PGauxiliaryFunctions|PGbasicmacros|PGanswermacros|MathObjects|PGcourse|AnswerFormatHelp).pl/ } - map {s/['"]//gr} split(/\s+|\s*,\s*/, $3); + map {s/['"]//gr} @macros; # Remove any duplicates: my %seen; @@ -141,7 +157,13 @@ sub convertToPGML { @macros = ('PGstandard.pl', 'PGML.pl', @macros, 'PGcourse.pl'); if ($qw_start) { - push(@all_lines, "loadMacros(qw$qw_start\n\t" . join("\n\t", @macros) . "\n$qw_end);"); + if ($num_macro_lines > 1) { # put each macro on a separate line + push(@all_lines, "loadMacros(qw$qw_start"); + push(@all_lines, "\t$_") for (@macros); + push(@all_lines, "$qw_end);"); + } else { + push(@all_lines, "loadMacros(qw$qw_start" . join(' ', @macros) . "$qw_end);"); + } } else { push(@all_lines, 'loadMacros(' . join(', ', map {"'$_'"} @macros) . ');'); }