GithubHelp home page GithubHelp logo

Currency errors with hardcopy about pg HOT 14 CLOSED

pstaabp avatar pstaabp commented on July 20, 2024
Currency errors with hardcopy

from pg.

Comments (14)

drgrice1 avatar drgrice1 commented on July 20, 2024

Use [`[$c]`] instead. This works for both HTML and hardcopy.

from pg.

Alex-Jordan avatar Alex-Jordan commented on July 20, 2024

It's not intuitive, but I think this is expected behavior.

[$c] gives the string interpolation for $c, which is \$1,000.00. Then PGML has The value is \$1,000.00. and escapes the characters it needs to escape for the output mode. So for hardcopy, PGML turns the \ into {\ttfamily\char92} (a backslash) and the $ into \$. So you get The value is {\ttfamily\char92}\$1,000.00. Which you see in the PDF as \$1,000.00.

You could ask, should the string interpolation for $c be $1,000.00 instead? But then when you are not using PGML, that would break hardcopy production.

I think the "right" thing to do is use [$c]* to tell PGML "don't escape anything". Unless you think it's good to use math mode in that context, and then use @drgrice1 's suggestion [`[$c]`]. Of course neither is intuitively necessary to an author, but it seems like it is correct behavior.

Meanwhile with [$c] for HTML output, the backslash in \$1,000.00 is not "pre-escaped" to something like \char92. And it escapes the dollar sign to just a dollar sign. But then we escape that now and the output is <span class="tex2jax_ignore">$</span>1,000.00. which comes out looking fine.

Or with [$c]* for HTML output, you still get $1,000.00 but now you are saying not to wrap the dollar sign in a span. So that comes out fine too, since we do not use $ as a MathJax delimiter. (This raises the question for me though why are we wrapping $ in that span?)

My point is [$c]* works in both output forms.

I think this is not actually an issue, unless you have a way for something's string method to be different in a PGML context than it is in a non-PGML context. But it is a messy in-the-weeds PGML thing.

from pg.

Alex-Jordan avatar Alex-Jordan commented on July 20, 2024

This would be a messier problem if $c were something whose string method contains \$ (as it might be needed for non PGML hardcopy output) as well as some character that you want to escape for HTML. Fortunately the characters you want to escape for HTML (<>&) are already special LaTeX characters too. So it is unlikely that $c's string method would not already be handling those characters in a special way.

from pg.

Alex-Jordan avatar Alex-Jordan commented on July 20, 2024

I kept thinking about this and I see a way to change things to make the MWE work. It would set a questionable precedent.

In PGML.pl, there is a place where variable substitution happens. We can check the ref of the variable, and if it is Currency::Currency, then we could regex out the \$ to be just a plain $. I have tested this and it works. I'm hesitant to get PGML into the business of doing special things for special types of math objects, but we could do this.

In thinking about this, I realized that whenever we have an object whose string method contains a LaTeX control character and it is reasonable to use that object outside of math mode, then we have a similar problem. The only other situation like this I could think of is with a Percent from contextPercent.pl. Although the issue is worse there, because there the % is not escaped in the string method. So currently, if you use a Percent object outside of PGML, in regular non-math text, then you probably have a silent error with hardcopy production. (With the tail of a sentence getting treated as a LaTeX comment.) If the above edit to PGML is worth making, then I would probably change contextPercent.pl to escape the \% and add the same change to PGML for percents.

from pg.

Alex-Jordan avatar Alex-Jordan commented on July 20, 2024

Although the issue is worse there, because there the % is not escaped in the string method.

This is not true, I got mixed up. But we do have the same issue as with a Currency, where you see a backslash in hardcopy when you don't want to see one.

from pg.

dpvc avatar dpvc commented on July 20, 2024

The real issue, here, is that the Currency context mishandles the string values. This was a hack to get hardcopy to work some 16 years ago in BEGIN_TEXT/END_TEXT blocks, where you would just use $c within the text, and it needed to have \$ when inserted into the string. But it was probably a mistake, and certainly makes it harder to use in PGML.

I offer the following potential solution to this. A bit of background first. There are three output methods for MathObjects, $mo->string and $mo->TeX are the well known ones, and these should produce an ASCII string version of the MathObject and a TeX version suitable for use within math-mode. The string() method really shouldn't be altering that for when $displayMode is TeX. The third method is less well know, which is $mo->stringify, which gets called whenever a MathObject is used in a string context. The default method is in pg/lib/Value.pm

pg/lib/Value.pm

Lines 997 to 1001 in daff8af

sub stringify {
my $self = shift;
return $self->TeX if Value->context->flag('StringifyAsTeX');
return $self->string;
}

and its purpose was to use $mo->TeX when the context flag StringifyAsTeX is true, and $mo->string otherwise. This flag is controlled by the Context()->texStrings and context->normalStrings methods, and was a means of trying to simplify the use of MathObejcts within BEGIN_TEXT/END_TEXT blocks (so you didn't have to do things like \(\{$c->TeX\}\) and could just do \($c\) to get the TeX version when Context()->texStrings is true.

One of the reasons I wrote PGML was to make the insertion of a MathObject's value be sensitive to the context in which it is used: if in a math block, it would use the TeX method, and otherwise would stringily it as normal. That avoids having to use texStrings and normalStrings, which were not granular enough to really work well (everything in the output would have to be one or the other).

So my proposal is to remove the TeX quoting from the Currency's string method, and move it to the stringify method instead. That way, when a currency is used in a string context (like in BEGIN_TEXT/END_TEXT), it will get the quoting, as it does now, but when the string method is called, it doesn't. Then, PGML should call string explicitly (which it doesn't currently do), since it will protect the TeX characters in the output later. That will mean you won't get the extra backslash in hardcopy output from PGML, but problems that still use BEGIN_TEXT/END_TEXT will still work.

Here is a diff that implements this.

diff --git a/macros/contexts/contextCurrency.pl b/macros/contexts/contextCurrency.pl
index ed2d9f0d..3fa03f44 100644
--- a/macros/contexts/contextCurrency.pl
+++ b/macros/contexts/contextCurrency.pl
@@ -250,11 +250,7 @@ sub new {
                        precedence    => 10,
                        associativity => $associativity,
                        type          => "unary",
-                       string        => (
-                               ($main::displayMode eq 'TeX' or $main::displayMode eq 'PTX')
-                               ? Currency::quoteTeX($symbol)
-                               : $symbol
-                       ),
+                       string        => $symbol,
                        TeX   => Currency::quoteTeX($symbol),
                        class => 'Currency::UOP::currency'
                },
@@ -269,6 +265,7 @@ sub new {
                forceDecimals     => 0,
                noExtraDecimals   => 1,
                trimTrailingZeros => 0,
+               legacyTeXStrings  => 0,
        );
        $context->{_initialized} = 1;
        $context->update;
@@ -342,7 +339,7 @@ sub addSymbol {
                        $symbol => {
                                %{$def},
                                associativity => $associativity,
-                               string        => ($main::displayMode eq 'TeX' ? Currency::quoteTeX($string) : $string),
+                               string        => $string,
                                TeX           => Currency::quoteTeX($string),
                        }
                );
@@ -380,7 +377,7 @@ sub update {
        $context->operators->set(
                $data->{symbol} => {
                        associativity => $data->{associativity},
-                       string        => ($main::displayMode eq 'TeX' ? Currency::quoteTeX($string) : $string),
+                       string        => $string,
                        TeX           => Currency::quoteTeX($string),
                }
        );
@@ -526,6 +523,8 @@ sub format {
        my $currency = ($self->{currency} || $self->context->{currency});
        my ($symbol, $comma, $decimal) = ($currency->{symbol}, $currency->{comma}, $currency->{decimal});
        $symbol = $self->context->operators->get($symbol)->{$type} || $symbol;
+        $symbol = Currency::quoteTeX($symbol) if $self->context->flag('legacyTeXStrings') &&
+                                                 $type eq 'string' && $main::displayMode eq 'TeX';
        $comma  = "{$comma}" if $type eq 'TeX';
        my $s = ($self->value >= 0 ? "" : "-");
        my $c = main::prfmt(CORE::abs($self->value), "%.2f");
@@ -535,6 +534,14 @@ sub format {
        $c = ($currency->{associativity} eq "right" ? $s . $c . $symbol : $s . $symbol . $c);
        $c =~ s/^\s+|\s+$//g;
        return $c;
+      }
+
+sub stringify {
+       my $self = shift;
+       return $self->TeX if $self->context->flag('StringifyAsTeX');
+        my $legacy = $self->context->flag('legacyTeXStrings');
+       my $string = $self->string;
+        return $main::displayMode eq 'TeX' && !$legacy ? Currency::quoteTeX($string) : $string;
 }
 
 sub string { (shift)->format("string") }
diff --git a/macros/core/PGML.pl b/macros/core/PGML.pl
index 15b76b8a..8b5b1890 100644
--- a/macros/core/PGML.pl
+++ b/macros/core/PGML.pl
@@ -934,10 +934,10 @@ sub replaceVariable {
        my ($result, $error) = PGML::Eval($var);
        PGML::Warning "Error evaluating variable \$$item->{text}: $error" if $error;
        $result = "" unless defined $result;
-       if ($block->{type} eq 'math' && Value::isValue($result)) {
-               if   ($block->{parsed}) { $result = $result->string }
-               else                    { $result = '{' . $result->TeX . '}' }
-       }
+        if (Value::isValue($result)) {
+          $result = ($block->{type} eq 'math' && !$block->{parsed} ?
+                     '{' . $result->TeX . '}' : $result->string);
+        }
        return $result;
 }

I've added a legacyTeXStrings flag that makes Current work essentially as it did before, so if anyone really needed the old behavior, you could get that.

The only possible situation that I can see that might be problematic is if you inserted a Currency value into a larger (non-PGML string), like $message = "The cost is $c.", which will include the backslash when $displayMode is TeX. If you then use [$message] in PGML, you will get the extra backslash. But since you can use [$message]* in this case, I'm not that worried.

from pg.

Alex-Jordan avatar Alex-Jordan commented on July 20, 2024

I'm testing @dpvc's diff and it works for the reported issue. One thing that it "breaks" is that currently a problem might have:

The value is [$c]*.

which works currently (both in HTML and hardcopy). In hardcopy the star prevents any escaping applied to the current output which is \$. With the changes here, using the star breaks the hardcopy. Now there is only a $ which does not get escaped.

I'm OK making the change. If this breaks a problem somewhere, they can remove the star. (I would think setting legacyTeXStrings => 1 would also work for that situation, but testing, it does not.) I'll open a PR with Davide's diff for continued discussion or merging.

from pg.

dpvc avatar dpvc commented on July 20, 2024

I would think setting legacyTeXStrings => 1 would also work for that situation, but testing, it does not.

Hmmm, it seems to do what I expect it should. For HTML output, I get

<div class="PGML">
$10.00</div>

for [$c]*, which is what you would have gotten in the past, and for hardcopy, I see

{\pgmlSetup
\$10.00\par}%

with the quoted dollar sign. I'll have to try your PR.

from pg.

dpvc avatar dpvc commented on July 20, 2024

I checked your branch and get the results the same as above. This is all with my command-line tool, so I'll have to try in an actual WeBWorK problem.

from pg.

Alex-Jordan avatar Alex-Jordan commented on July 20, 2024

OK, sorry for the distraction. It turns out I was testing with

Context('Currency')->flags(legacyTeXStrings => 1);

instead of

Context('Currency')->flags->set(legacyTeXStrings => 1);

which doesn't throw an error and I was oblivious to not setting the flag correctly.

from pg.

dpvc avatar dpvc commented on July 20, 2024

which doesn't throw an error

Right. The Context()->flags method takes no arguments, and returns the flags object, but since perl doesn't throw errors when the argument list isn't correct, you would get no error for that. Sorry you had to deal with that.

from pg.

dpvc avatar dpvc commented on July 20, 2024

Some additional information: a quick search of the contents of pg/macros suggests that there are no other macros that change their string method output based on the $displayMode, so Currency is the only one that needs fixed in this way. In particular, the Percent context doesn't quote the % when a percent variable is stringified, and so that should cause problems in hardcopy when used outside of PGML. So you may want to add a similar stringify method to the contextPercent.pl file so that it will get proper output in hardcopy in BEGIN_TEXT/END_TEXT blocks. It should work properly in PGML already.

from pg.

Alex-Jordan avatar Alex-Jordan commented on July 20, 2024

and so that should cause problems in hardcopy when used outside of PGML

It does indeed. I verified this recently. A difference is that it doesn't cause tex compilation to fail in most cases. It just comments out the rest of the line, which is typically just the end of a sentence. So it's a silent fail.

Once #981 is resolved, I do plan to add something similar for contextPercent.

from pg.

pstaabp avatar pstaabp commented on July 20, 2024

Fixed in #981

from pg.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.