bugGNU roff - Bugs: bug #65474, [troff] spurious "warning:...

 
 

bug #65474: [troff] spurious "warning: unbalanced 'el' request" when formatting zic(8) from TZDB project

Submitter:  Paul Eggert <eggert>
Submitted:  Sun 17 Mar 2024 05:35:43 PM UTC
   
 
Category:  Core Severity:  3 - Normal
Item Group:  Warning/Suspicious behaviour Status:  Fixed
Privacy:  Public Assigned to:  gbranden
Open/Closed:  Closed Planned Release:  1.24.0
* Mandatory Fields

Add a New Comment Rich Markup
   

Jump to the original submission

Sat 13 Apr 2024 09:57:59 PM UTC, comment #28: 


commit b2ea8a3d84929baa4ee9f74db07113a3ce50fa4e
Author: G. Branden Robinson <g.branden.robinson@gmail.com>
Date:   Fri Apr 5 19:23:28 2024 -0500

    [troff]: Retire "el" warning category.

    It's much too hard to perform this analysis without the risk of throwing
    this warning spuriously.  Paul Eggert reported a real-world use of
    `ie`/`el` requests nested without the use of brace escape sequences in
    his zic(8) man page that is structurally equivalent to the following
    (with indentation added for clarity).

    .ie \nA .tm register A is truthy
    .el .ie t .tm in typesetter mode
    .    el   .tm in terminal mode

    Without brace escape sequences, the `skip_branch{}` function that
    discards input corresponding to control flow branches not taken does not
    reliably keep track of the nesting level.  So why not just make
    `skip_branch()` more sophisticated to handle this case?  Because it
    doesn't generalize.  What if the input changes the control character, or
    uses the no-break control character?  What if the input has renamed the
    `ie` request or invokes it through a macro?

    * src/roff/troff/input.cpp: Drop this warning category from the
      `warning_table` global.

      (else_request): Stop throwing it.

    * src/roff/troff/troff.h: Comment out its value in the `warning_type`
      enum.

    Fixes <https://savannah.gnu.org/bugs/?65474>.  Thanks to Paul Eggert for
    the report.


G. Branden Robinson <gbranden>
Group administrator
Sun 07 Apr 2024 05:03:43 AM UTC, comment #27: 

comment #25:

> comment #22:
> > Sure, but I'd like to do that once the 3 of us (you, me, and
> > Paul) are on the same page, so that we aren't arguing any
> > facts, just the advisability of future action.
> I guess it's legal in the sense that it has a well-defined behavior.  But it certainly doesn't do what any reasonable reader would expect,


I urge you to take the exhibit to some *roff users of long experience and measure their expectations.

> and an in-document condition being able to derail the parsing of flow-control constructs is less than optimal.


I don't think anything's getting derailed here.  *roff simply isn't paying off the expectations of C programmers.
 

> > That is why I worked on bug #45502 before this one and do
> > propose to change GNU troff there to align with AT&T behavior.
>
> OK.  That should fix the comment #11 discrepancy.  I don't see it affecting the comment #0 one, as that contains no empty branches.


There is no such thing as an empty branch.  A branch that looks empty has a newline on it, which if taken puts a word break (if filling) or line break (if not filling) on the output.

The very idea that *roff admits an "empty branch" must be banished from the mind if one wants to model *roff behavior.


$ cat EXPERIMENTS/empty-branch.roff
foo\c
.if \nA \{\}
bar
$ nroff EXPERIMENTS/empty-branch.roff | cat -s
foobar

$ nroff -rA1 EXPERIMENTS/empty-branch.roff | cat -s
foo bar


The braces are as empty as you please but there is still a newline after them.  And nota bene, that exhibit works the same in GNU troff 1.22.4, 1.23.0, Git HEAD, and my working copy.

> > The problem is that this warning is only sometimes valid.
>
> But that's the problem inherent in the logic used in bug #59434 being only sometimes valid.  The warning alerts the user (certainly alerted me) that the code isn't processed as expected.


It will alert the user to (likely) unexpected flow of control in some cases; in others it will spuriously warn, as here.

I think we should not tolerate the issue of spurious warnings.

comment #26:

> comment #24:
> > I think my recast[1] of the relevant material in our Texinfo
> > manual is a reliable guide to interpretation.
>
> It's a reliable guide to other-troff interpretation.  But in groff, change that example's ".nr force-word-break" from 1 to 0 and you crash up against bug #45502.


Hence my pending change, documented there, that will bring consistent behavior and universal harmony.  ;-)

G. Branden Robinson <gbranden>
Group administrator
Sun 07 Apr 2024 04:28:05 AM UTC, comment #26: 

comment #24:

> I think my recast[1] of the relevant material in our Texinfo
> manual is a reliable guide to interpretation.


It's a reliable guide to other-troff interpretation.  But in groff, change that example's ".nr force-word-break" from 1 to 0 and you crash up against bug #45502.

Dave <barx>
Group Member
Sun 07 Apr 2024 04:21:03 AM UTC, comment #25: 

comment #22:

> Sure, but I'd like to do that once the 3 of us (you, me, and
> Paul) are on the same page, so that we aren't arguing any
> facts, just the advisability of future action.


👍

> Yours and Bjarni's claims in that ticket are, I fear, incorrect.
> The input proffered there is legal/valid, and moreover is
> interpreted the same in all tested AT&T and GNU _troff_s.


I guess it's legal in the sense that it has a well-defined behavior.  But it certainly doesn't do what any reasonable reader would expect, and an in-document condition being able to derail the parsing of flow-control constructs is less than optimal.

> That is why I worked on bug #45502 before this one and do
> propose to change GNU troff there to align with AT&T behavior.


OK.  That should fix the comment #11 discrepancy.  I don't see it affecting the comment #0 one, as that contains no empty branches.

> The problem is that this warning is only sometimes valid.


But that's the problem inherent in the logic used in bug #59434 being only sometimes valid.  The warning alerts the user (certainly alerted me) that the code isn't processed as expected.

Dave <barx>
Group Member
Sun 07 Apr 2024 04:15:47 AM UTC, comment #24: 

comment #23:

> comment #11:
> > Solaris 10 /usr/bin/nroff outputs "CASE a", which is what I'd expect.
>
> Heirloom troff does as well.
>
> > gnroff outputs "NOTREACHED", which is surprising.
>
> My guess at the end of comment #16 was wrong.  This, it turns out, is an application of bug #45502.  In a nutshell, groff diverges from other troffs when given an .if or .ie with an empty branch (no content after the conditional), which is what the ". ie '1'2'" line has.  Put any content after that test, and all the troffs produce the same output.


I think my recast[1] of the relevant material in our Texinfo manual is a reliable guide to interpretation.


5.23.2 if-then
--------------

 -- Request: .if cond-expr anything
     Evaluate the conditional expression COND-EXPR, and if it evaluates
     true (or to a positive value), interpret the remainder of the line
     ANYTHING as if it were an input line.  Recall from *note Invoking
     Requests:: that any quantity of spaces between arguments to
     requests serves only to separate them; leading spaces in ANYTHING
     are thus not seen.  ANYTHING effectively _cannot_ be omitted; if
     COND-EXPR is true and ANYTHING is empty, the newline at the end of
     the control line is interpreted as a blank input line (and
     therefore a blank text line).

          super\c
          tanker
          .nr force-word-break 1
          super\c
          .if ((\n[force-word-break] = 1) & \n[.int])
          tanker
              => supertanker super tanker


[1] from September 2022, commit 66b7cb51ba

G. Branden Robinson <gbranden>
Group administrator
Sun 07 Apr 2024 03:45:45 AM UTC, comment #23: 

comment #11:

> Solaris 10 /usr/bin/nroff outputs "CASE a", which is what I'd expect.


Heirloom troff does as well.

> gnroff outputs "NOTREACHED", which is surprising.


My guess at the end of comment #16 was wrong.  This, it turns out, is an application of bug #45502.  In a nutshell, groff diverges from other troffs when given an .if or .ie with an empty branch (no content after the conditional), which is what the ". ie '1'2'" line has.  Put any content after that test, and all the troffs produce the same output.

Dave <barx>
Group Member
Sun 07 Apr 2024 03:16:38 AM UTC, comment #22: 

comment #19:

> comment #18:
> > I propose to fix this by killing the warning category along with
> > the spuriousness.
>
> With two longtime groff users (Tadziu and Bjarni, with me still on the fence) arguing that the warning isn't spurious, I wonder if this proposal should be put before a wider audience.


Sure, but I'd like to do that once the 3 of us (you, me, and Paul) are on the same page, so that we aren't arguing any facts, just the advisability of future action.
 

> I think clearly documenting the situation described in bug #59434 would greatly simplify explaining to users why this warning occurs and how to code for groff's nonintuitive flow control.


Okay.  Yours and Bjarni's claims in that ticket are, I fear, incorrect.  The input proffered there is legal/valid, and moreover is interpreted the same in all tested AT&T and GNU _troff_s.

The "scope" of the `if` request in question isn't "two requests".  It's one, because there are no brace escape sequences.  The `if` governs the one `ie` and then is done.

> If there are incompatibilities between how groff handles .if/.ie/.el and how ancestral and peer troffs do,


That is why I worked on bug #45502 before this one and do propose to change GNU troff there to align with AT&T behavior.

For this ticket, I propose no change in how input is interpreted.  What differs is the emission of a diagnostic message that has to be opted into.  AT&T troff simply did not expose anything like a notion of the "nesting level of if/else control structures" to the user.  GNU troff does, via this one channel.

> those should also be fixed or documented


I have a fix in my working copy, and aim to document this behavior as well, possibly with something similar to the "supertanker" example already extant in our Texinfo manual.

> before axing arguably valid warnings.


The problem is that this warning is only sometimes valid.  We could talk about bringing it back once I have the "style" warning category set up, and possibly repair the situation by warning in any situation where the input nests `if` or `ie` requests without using brace escape sequences.  Whether that is tractable depends a lot on how complex it gets to read ahead in the input in the `skip_branch()` function (presently called `skip_alternative()`, but I have a rename pending in my working copy).

https://git.savannah.gnu.org/cgit/groff.git/tree/src/roff/troff/input.cpp?h=1.23.0#n5700

Anyway, do we agree on the facts?

G. Branden Robinson <gbranden>
Group administrator
Sun 07 Apr 2024 02:48:05 AM UTC, comment #21: 

comment #20:

> I can't explain why .MX's two levels of .ie/.el work, whereas .MY's three levels do not work; can anyone else explain it?

Never mind, I now see the missing "\}" after the ".el D" line. Sorry about the noise in my previous comment.

Paul Eggert <eggert>
Sun 07 Apr 2024 02:40:03 AM UTC, comment #20: 

I confess that I still don't know how .ie and .el work, despite the attempts in the documentation (and in this bug report) to explain them. For example, given the following test of nesting .ie and .el two deep (in .MX) and three deep (in .MY):

.pl 1
.de MX
\\$1 \\$2
.ie \\$1 \{\
. ie \\$2 a
. el b
.\}
.el \{\
. ie \\$2 c
. el d
.\}
z
.br
..
.MX 1 1
.MX 1 0
.MX 0 1
.MX 0 0
.de MY
\\$1 \\$2 \\$3
.ie \\$1 \{\
. ie \\$2 \{\
.  ie \\$3 A
.  el B
. \}
. el \{\
.  ie \\$3 C
.  el D
.\}
.el \{\
. ie \\$2 \{\
.  ie \\$3 E
.  el F
. \}
. el \{\
.  ie \\$3 G
.  el H
. \}
.\}
Z
.br
..
.MY 1 1 1
.MY 1 1 0
.MY 1 0 1
.MY 1 0 0
.MY 0 1 1
.MY 0 1 0
.MY 0 0 1
.MY 0 0 0


groff -Tascii -ww and Solaris 10 nroff both output the following without any warnings on groff's part:

1 1 a z
1 0 b z
0 1 c z
0 0 d z
1 1 1 A Z
1 1 0 B Z
1 0 1 C Z
1 0 0 D Z
0 1 1


I can't explain why .MX's two levels of .ie/.el work, whereas .MY's three levels do not work; can anyone else explain it? If so, it'd be helpful if the explanation were in the manual.

I am starting to think that man pages should never use .ie as that might simplify portability.

Paul Eggert <eggert>
Sat 06 Apr 2024 09:24:11 PM UTC, comment #19: 

comment #18:

> I propose to fix this by killing the warning category along with
> the spuriousness.


With two longtime groff users (Tadziu and Bjarni, with me still on the fence) arguing that the warning isn't spurious, I wonder if this proposal should be put before a wider audience.

I think clearly documenting the situation described in bug #59434 would greatly simplify explaining to users why this warning occurs and how to code for groff's nonintuitive flow control.

If there are incompatibilities between how groff handles .if/.ie/.el and how ancestral and peer troffs do, those should also be fixed or documented before axing arguably valid warnings.

Dave <barx>
Group Member
Sat 06 Apr 2024 12:26:13 AM UTC, comment #18: 

I propose to fix this by killing the warning category along with the spuriousness.


commit ab22aa4495616f878e33231ee07bbd6808967a9a (HEAD -> master)
Author: G. Branden Robinson <g.branden.robinson@gmail.com>
Date:   Fri Apr 5 19:23:28 2024 -0500

    [troff]: Retire "el" warning category.

    It's much too hard to perform this analysis without the risk of throwing
    this warning spuriously.  Paul Eggert reported a real-world use if
    `ie`/`el` request nested without the use of brace escape sequences in
    his zic(8) man page that is structurally equivalent to the following.

    .ie \nA .tm register A is truthy
    .el .ie t .tm in typesetter mode
    .el .tm in terminal mode

    Without brace escape sequences, the `skip_branch{}` function that
    discards input corresponding to control flow branches not taken does not
    reliably keep track of the nesting level.  So why not just make
    `skip_branch()` more sophisticated to handle this case?  Because it
    doesn't generalize.  What if the input changes the control character, or
    uses the no-break control character?  What if the input has renamed the
    `ie` request or invokes it through a macro?

    * src/roff/troff/input.cpp: Drop this warning category from the
      `warning_table` global.

      (else_request): Stop throwing it.

    * src/roff/troff/troff.h: Comment out its value in the `warning_type`
      enum.

    Fixes <https://savannah.gnu.org/bugs/?65474>.  Thanks to Paul Eggert for
    the report.

    NEWS: Add item.

diff --git a/ChangeLog b/ChangeLog
index 2c0291e46..fd4a8b652 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,35 @@
+2024-04-05  G. Branden Robinson <g.branden.robinson@gmail.com>
+
+       [troff]: Retire "el" warning category.
+
+       It's much too hard to perform this analysis without the risk of
+       throwing this warning spuriously.  Paul Eggert reported a
+       real-world use if `ie`/`el` request nested without the use of
+       brace escape sequences in his zic(8) man page that is
+       structurally equivalent to the following.
+
+       .ie \nA .tm register A is truthy
+       .el .ie t .tm in typesetter mode
+       .el .tm in terminal mode
+
+       Without brace escape sequences, the `skip_branch{}` function
+       that discards input corresponding to control flow branches not
+       taken does not reliably keep track of the nesting level.  So why
+       not just make `skip_branch()` more sophisticated to handle this
+       case?  Because it doesn't generalize.  What if the input changes
+       the control character, or uses the no-break control character?
+       What if the input has renamed the `ie` request or invokes it
+       through a macro?
+
+       * src/roff/troff/input.cpp: Drop this warning category from the
+       `warning_table` global.
+       (else_request): Stop throwing it.
+       * src/roff/troff/troff.h: Comment out its value in the
+       `warning_type` enum.
+
+       Fixes <https://savannah.gnu.org/bugs/?65474>.  Thanks to Paul
+       Eggert for the report.
+
 2024-04-05  G. Branden Robinson <g.branden.robinson@gmail.com>

        * src/roff/troff/input.cpp (skip_branch): Handle a degenerate
diff --git a/NEWS b/NEWS
index 240cf1414..b941db9b1 100644
--- a/NEWS
+++ b/NEWS
@@ -29,6 +29,14 @@ o The `color`, `cp`, `linetabs`, and `vpt` requests now interpret
   with a register interpolation as an argument, the outcome agrees with
   an `if` test of the register's value.

+o The formatter no longer reports warnings in the "el" category.  There
+  was only one; it threw a warning diagnostic if enabled (which it was
+  not by default) if it inferred an imbalance between `ie` and `el`
+  requests.  Unfortunately it wasn't reliable and sometimes spuriously
+  issued these warnings, and making it perfectly reliable did not look
+  tractable.  We recommend using brace escape sequences `\{` and `\}` to
+  ensure that your control flow structures remain maintainable.
+
 o In nroff mode (in other words, when producing output for devices that
   claim to be terminals), the formatter now reports warning diagnostics
   regarding certain output problems using units of lines instead of
diff --git a/src/roff/troff/input.cpp b/src/roff/troff/input.cpp
index 5c53217fc..db61117ea 100644
--- a/src/roff/troff/input.cpp
+++ b/src/roff/troff/input.cpp
@@ -6111,10 +6111,8 @@ static void if_request()

 static void else_request()
 {
-  if (if_else_stack.empty()) {
-    warning(WARN_EL, "unbalanced 'el' request");
+  if (if_else_stack.empty())
     skip_branch();
-  }
   else {
     bool predicate = if_else_stack.top();
     if_else_stack.pop();
@@ -8927,7 +8925,6 @@ static struct {
   { "range", WARN_RANGE },
   { "break", WARN_BREAK },
   { "delim", WARN_DELIM },
-  { "el", WARN_EL },
   { "scale", WARN_SCALE },
   { "number", WARN_NUMBER },
   { "syntax", WARN_SYNTAX },
diff --git a/src/roff/troff/troff.h b/src/roff/troff/troff.h
index e687464ce..c2f37a034 100644
--- a/src/roff/troff/troff.h
+++ b/src/roff/troff/troff.h
@@ -62,7 +62,7 @@ enum warning_type {
   WARN_NUMBER = 02,
   WARN_BREAK = 04,
   WARN_DELIM = 010,
-  WARN_EL = 020,
+  //WARN_EL = 020,
   WARN_SCALE = 040,
   WARN_RANGE = 0100,
   WARN_SYNTAX = 0200,


G. Branden Robinson <gbranden>
Group administrator
Fri 05 Apr 2024 10:16:23 PM UTC, comment #17: 

  My posting (comment #14) was an answer to comment #11 (Paul Eggert).

  The example, with a possible explanation

.pl 3
.de MY
.ie     '\\$1'a' CASE a
.el .ie '\\$1'b' CASE b
.el              CASE z
..
.ie '1'1' \{\
. ie '1'2' .el .MY a\" groff processes this according to the output
.\". el .MY a
.\}
.el NOTREACHED

The ".ie '1'2' .el .MY a" is probably processed as

".ie '1'2' & .el .MY a"

  Tested with that line.

  So the probable execution is

.ie '1'1 \{\
\" true
.ie '1'2' .el .MY a\" false
.\}
.el NOTREACHED

.NB.  The whole text can be used as a input to nroff.

Bjarni Ingi Gislason <bjarniig>
Fri 05 Apr 2024 06:33:09 PM UTC, comment #16: 

comment #11:

> Could you explain why Tadziu's argument is cogent? I'm not
> following it.


In a nutshell, groff short-circuits in a quirky and nonintuitive way.  In this code:

.ie condition1 action1
.el .ie condition2 action2
.el action3

when condition1 is true, groff takes action1.  Then it sees the first .el, a branch it now knows it needn't take, so it discards the rest of the line unexamined.  Thus, it's unaware of the second .ie.  So when it goes on and next sees the second .el, it says, "Waitaminute, else of what?"

> Also, is groff purposely incompatible with traditional nroff
> when evaluating .ie?


I don't think so, and I think this incompatibility is what Branden is getting at in bug #60260, though I've not yet grokked his logic there to know if it's the same as what your example shows.

Dave <barx>
Group Member
Fri 05 Apr 2024 03:07:40 PM UTC, comment #15: 

comment #14:

>   The example shows a bug in groff or an interpretation as in some
> other language.


This comment is not helpful when you don't specify which example you're talking about.  There are several in this ticket's traffic.

>   That interpretation is not mentioned in
> "https://troff.org/54.pdf" (Troff User's Manual, Nov 1992;
> chapter 16).


Despite its status as religious scripture among some groff mailing list subscribers, that document is not a complete specification of the language.

Where it is lacking, we must supplement it with observations of extant implementations.

And even then we are not bound by the precedents of those implementations.  But if groff's behavior differs, it should do so for a good reason.
 

>   The 'ie' request has nothing to process on the same (logical) line,


A newline is not nothing.  It is syntactically significant, and puts a break on the output.

> so it grabs the next line.


The "grabbing" of the next line is because that's how the formatter operates even in the absence of control flow requests.

>   Similar "skipping the new line character" is also shown with
> '\{' at the end of a line, instead of '\{\' (block begins on the
> next line).


The newline is not "skipped" in the former case, but becomes a break.

Please do more research before opining further on this ticket.

G. Branden Robinson <gbranden>
Group administrator
Fri 05 Apr 2024 02:57:32 PM UTC, comment #14: 

  The example shows a bug in groff or an interpretation as in some
other language.

  That interpretation is not mentioned in
"https://troff.org/54.pdf" (Troff User's Manual, Nov 1992;
chapter 16).

  The 'ie' request has nothing to process on the same (logical) line,
so it grabs the next line.

Test, by adding some text on the same (logical) line as the 'ie'
request.

N.B.

  Similar "skipping the new line character" is also shown with
'\{' at the end of a line, instead of '\{\' (block begins on the
next line).

Bjarni Ingi Gislason <bjarniig>
Fri 05 Apr 2024 02:47:04 PM UTC, comment #13: 

Nope, I'm sorry, the original problem reported had to do with the zic(8) man page.

My fix for bug #45502 doesn't resolve that.


$ ./build/test-groff -z -ww -m an -T ascii ../tz/zic.8
troff:../tz/zic.8:357: warning: unbalanced 'el' request


My apologies for the noise.  Reopening.

G. Branden Robinson <gbranden>
Group administrator
Fri 05 Apr 2024 02:36:01 PM UTC, comment #12: 

I had an inkling that I needed to work on bug #45502 before tackling this one, and my inkling paid off.

With the fix applied in that ticket, Paul's exhibit below behaves as he expects.


$ cat EXPERIMENTS/eggert4.roff
.pl 1
.de MY
.ie     '\\$1'a' CASE a
.el .ie '\\$1'b' CASE b
.el              CASE z
..
.ie '1'1' \{\
. ie '1'2'
. el .MY a
.\}
.el NOTREACHED
$ ~/groff-1.22.3/bin/nroff EXPERIMENTS/eggert4.roff
NOTREACHED
$ ~/groff-stable/bin/nroff EXPERIMENTS/eggert4.roff # 1.23.0
NOTREACHED
$ ~/groff-HEAD/bin/nroff EXPERIMENTS/eggert4.roff # most recent push
NOTREACHED
$ ./build/test-groff -ww -T ascii EXPERIMENTS/eggert4.roff # my working copy
troff:EXPERIMENTS/eggert4.roff:11: warning: unbalanced 'el' request
CASE a


I think the warning about line 11 is legit.

Closing as duplicate of bug #45502.

G. Branden Robinson <gbranden>
Group administrator
Fri 05 Apr 2024 04:08:59 AM UTC, comment #11: 

comment #10:

> comment #9:
> Tadziu makes a cogent argument (http://lists.gnu.org/r/groff/2021-03/msg00024.html) that the warning is not spurious.

Could you explain why Tadziu's argument is cogent? I'm not following it.

Also, is groff purposely incompatible with traditional nroff when evaluating .ie? Because that's what I'm observing. Consider the following input:

.pl 1
.de MY
.ie     '\\$1'a' CASE a
.el .ie '\\$1'b' CASE b
.el              CASE z
..
.ie '1'1' \{\
. ie '1'2'
. el .MY a
.\}
.el NOTREACHED


Solaris 10 /usr/bin/nroff outputs "CASE a", which is what I'd expect. gnroff outputs "NOTREACHED", which is surprising. Tadziu's comment, if I understand it correctly, says this should output both "CASE a" and "NOTREACHED", which would be more surprising still.

Is there a simple example input that illustrates why Tadziu's comment makes sense?

Paul Eggert <eggert>
Fri 05 Apr 2024 12:11:26 AM UTC, comment #10: 

comment #9:

> I'll leave this one to cope with the issue of when we should
> throw a warning, how.


Tadziu makes a cogent argument (http://lists.gnu.org/r/groff/2021-03/msg00024.html) that the warning is not spurious.  Bjarni might be trying to make the same point, but, like John Gardner, I find his explanation... less cogent.  (John's reply (http://lists.gnu.org/r/groff/2024-04/msg00016.html) is in the thread Bjarni mentions in comment #7.)

My C analogy (comment #5) fails because, while the C compiler parses all if/then/else branches, the groff interpreter sometimes skips over some of them.  I seemingly knew this at one time (evidence: bug #59434), but my brain, like the groff parser, skipped over that when composing comment #5.

Dave <barx>
Group Member
Thu 04 Apr 2024 12:12:22 PM UTC, comment #9: 

Dave's right.

See bug #60260.

There would appear to be 3 issues here, so I won't actually close any of these as duplicates.

Bug #59434 says that groff documentation doesn't adequately explain this stuff.  I am now not surprised, since groff matches `el` request to `ie` ones (in the absence of explicit bracing, at least), differently from AT&T troff.  So someone trying to document this matter on the assumption that they behaved the same would likely run into trouble upon conducting simple experiments.

Bug #60260 regards the actual interpretation of the formatter's language, and control flow therein.

I'll leave this one to cope with the issue of when we should throw a warning, how.

At present, I suspect I'll seize the current `el` request for my desired `style` warning category.[1]  I partially agree with Bjarni that such diagnostics should exist.  I disagree with his discursive hectoring about how people should write their roff documents.  I'm comfortable exhorting the user with style warnings and advice in documentation, but not with treating the exercise of well-defined (if lousily documented) language semantics of 50 years' standing as errors.

To be concrete, I think the input:


$ cat EXPERIMENTS/el.roff
.el .tm this is unreachable code
$ nroff -ww EXPERIMENTS/el.roff
troff:EXPERIMENTS/el.roff:1: warning: unbalanced 'el' request


..._should_ warn, if style warnings are enabled.

But the first thing to cope with is #60260.  Let us then see if Paul's input makes noise at all.

[1] ...retaining recognition of "el" as an undocumented synonym of "style" to accommodate old scripts, yadda yadda yadda.

G. Branden Robinson <gbranden>
Group administrator
Thu 04 Apr 2024 07:09:32 AM UTC, comment #8: 

Upon further digging, this appears to be a duplicate of bug #60260.  Jim opened that bug report in 2021, but it's a restatement of an issue he previously brought up in 2013: http://lists.gnu.org/r/bug-groff/2013-02/msg00003.html

Dave <barx>
Group Member
Wed 03 Apr 2024 09:54:21 PM UTC, comment #7: 

  I sent my contribution to the groff list for more people to
read, with title

"An exercise for the brain(-software) (bug #65474)".

Bjarni Ingi Gislason <bjarniig>
Wed 03 Apr 2024 08:52:43 PM UTC, comment #6: 

  What I forgot to write:

Another language has polluted the code, as written.

Bjarni Ingi Gislason <bjarniig>
Tue 02 Apr 2024 11:28:10 PM UTC, comment #5: 

I agree with Paul that -ww is not intended as a style check.  But I further contend that this is not subpar style.

comment #3:

>   The translator is not happy about how the instructions are
> written, they are not informative enough for an unambiguous
> processing.


There is nothing ambiguous about the code in Paul's original example, either to a human reader who understands basic if/then/else syntax, or to a machine parser (theoretically, and seemingly in practice, except that something is confusing groff's parser into emitting a spurious warning).  Braces are not required for this construction in C.

#include <stdio.h>
main() {
  if (1 == 2)
    puts("Weird");
  else if (2 < 1)
    puts("Also weird");
  else puts("Normal");
}

When "if" statements are nested without braces, there can be ambiguity about which one an "else" applies to.  But there is none here.

Dave <barx>
Group Member
Tue 02 Apr 2024 10:11:45 PM UTC, comment #4: 

comment #3:

>   one can look at "groff" as being a (minimal) "code
> and style checker".

That's not the intent of groff -ww. The intent is to catch errors, not report style issues. The groff documentation says that -ww should issue this diagnostic only when "The 'el' request was encountered with no prior corresponding 'ie' request." In the TZDB man page there is a prior corresponding 'ie' request, therefore groff should not warn.

Although it might be helpful for groff to have a style checker, that should be a separate option, and it should not be enabled by default.

Paul Eggert <eggert>
Tue 02 Apr 2024 12:13:21 AM UTC, comment #3: 

  This is neither a spurious nor a "false positive" but a
legitimate remark about the code.

  I don't see a balance (like a two arm weight balance) with
separate left and right loads.

  The warning is falsely interpreted (translated) by humans.

  The translator is not happy about how the instructions are
written, they are not informative enough for an unambiguous
processing.

  The writer's duty is to supply the translator with all
necessary information to make its work efficient, correct and
without any doubt.

  When humans look at the code, they add (get, have) information
that the translator does not have.

.ie \n(.g groff
.el .ie t troff
.el neither groff nor troff

  So simply adding the needed information for a unique
interpretation is

.ie \n(.g groff
.el \{ .ie t troff
.el neither groff nor troff \}

  which is not visible enough and not an enough structured style,
changing to

.ie \n(.g groff
.el \{\
.  ie t troff
.  el neither groff nor troff
.\}

makes the "balance" visible at first glance.

  In this case one can look at "groff" as being a (minimal) "code
and style checker".

  The false interpretation (translation) of warnings by humans is
thus more common than one might suspect.

  Changing the code in "groff" to eliminate such a warning is
simply censorship and sabotage.

N.B.

  The showed warning "el" (code = 16) should be elevated to
a default status.

Bjarni Ingi Gislason <bjarniig>
Sun 17 Mar 2024 06:29:28 PM UTC, comment #2: 

Can reproduce.

I reduced Paul's exhibit further.


$ cat EXPERIMENTS/eggert2.roff
.ie \nA .tm register A is truthy
.el .ie t .tm in typesetter mode
.el .tm in terminal mode


In terms of the `tm` terminal messages above, I get equivalent results on all of groff 1.22.3, 1.22.4, 1.23.0, and git HEAD; Heirloom Doctools troff and nroff; DWB 3.3 troff and nroff; and Seventh Edition Unix troff and nroff.

I'll share output only the last since it's the most interesting in an archeological sense.


Restricted rights: Use, duplication, or disclosure
is subject to restrictions stated in your contract with
Western Electric Company, Inc.
Thu Sep 22 22:07:57 EDT 1988

login: dmr
$ cat > eggert2.roff
.ie \nA .tm register A is truthy
.el .ie t .tm in typesetter mode
.el .tm in terminal mode
$ nroff eggert2.roff
in terminal mode
$ nroff -rA1 eggert2.roff
register A is truthy
$ troff -t -rA1 eggert2.roff
@o`register A is truthy
`````````````````````````````````````````````````````````````````~@IAAAAAAAA$
$ troff -t eggert2.roff
@o`in typesetter mode
`````````````````````````````````````````````````````````````````~@IAAAAAAAA$ sync
$
login:
Simulation stopped, PC: 002306 (MOV (SP)+,177776)


The same spurious diagnostic is emitted in all cases with all tested versions of groff, though the correct branch is also taken in all cases.

Setting to "Minor" severity because it is only an annoyance and does no violence to formatting.

Still, would be nice to fix because spurious diagnostics reduce confidence in others.

G. Branden Robinson <gbranden>
Group administrator
Sun 17 Mar 2024 06:15:40 PM UTC, comment #1: 
Alejandro Colomar <alx>
Sun 17 Mar 2024 05:35:43 PM UTC, original submission:  

I am following up on a bug report against the TZDB documentation that turns out to be a bug in groff that causes groff to output a false positive in a diagnostic.

I reduced the bug to the following standalone shell command:

groff -ww >/dev/null <<'EOF'
.ie \n(.g groff
.el .ie t troff
.el neither groff nor troff
EOF

On my platform (Fedora 39, with groff 1.23.0) this outputs:

troff:<standard input>:3: warning: unbalanced 'el' request

However, there's nothing unbalanced here, and this input works fine with groff and with traditional troff.

Paul Eggert <eggert>

 

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:
   
   
Comment:
   

No files currently attached

 

Depends on the following items: None found

Items that depend on this one: None found

 

Carbon-Copy List
  • -email is unavailable- added by barx (Posted a comment)
  • -email is unavailable- added by bjarniig (Posted a comment)
  • -email is unavailable- added by gbranden (Posted a comment)
  • -email is unavailable- added by alx (Posted a comment)
  • -email is unavailable- added by eggert (Submitted the item)
  •  

    There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

    Only logged-in users can vote.

     

    Follow 16 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2024-04-13 gbranden StatusIn Progress Fixed
        Open/ClosedOpen Closed
        Planned ReleaseNone 1.24.0
    2024-04-06 gbranden StatusNone In Progress
    2024-04-05 gbranden StatusDuplicate None
        Open/ClosedClosed Open
    2024-04-05 gbranden Severity2 - Minor 3 - Normal
        StatusConfirmed Duplicate
        Assigned toNone gbranden
        Open/ClosedOpen Closed
    2024-04-04 gbranden Summaryspurious &quot;warning: unbalanced 'el' request&quot; when formatting zic(8) from TZDB project [troff] spurious "warning: unbalanced 'el' request" when formatting zic(8) from TZDB project
    2024-03-17 gbranden CategoryNone Core
        Severity3 - Normal 2 - Minor
        Item GroupNone Warning/Suspicious behaviour
        StatusNone Confirmed
        Summaryfalse positive &quot;warning: unbalanced 'el' request&quot; when processing TZDB zic.8 spurious "warning: unbalanced 'el' request" when formatting zic(8) from TZDB project

    Back to the top

    Powered by Savane 3.13-02a9.
    Corresponding source code