URL: <https://savannah.gnu.org/bugs/?68818>
Summary: [troff] equip input stream reading ("lexing")
functions with a context argument
Group: GNU roff
Submitter: gbranden
Submitted: Thu 22 Oct 2026 03:37:07 PM UTC
Category: Formatter
Severity: 1 - Wish
Item Group: Warning/Suspicious behaviour
Status: None
Privacy: Public
Assigned to: None
Open/Closed: Open
Discussion Lock: Unlocked
Planned Release: None
_______________________________________________________
Follow-up Comments:
-------------------------------------------------------
Date: Thu 22 Oct 2026 03:37:07 PM UTC By: G. Branden Robinson <gbranden>
Consider for _groff_ 1.26.
I've complained before about how GNU _troff_'s parser design often loses
detail where it could be really helpful, as with numeric expression parsing.
Request and escape sequence handlers know the input context, and readily
identify the nature of the input being interpreted. That's good stuff to pass
along to the user in the event of a diagnostic message.
It occurs to me that in most cases where such a handler delegates its
interpretation of the input stream to a utility function, that utility
function is usually name something like `read_*()` and returns a `bool`.
So, we could enhance these functions to take a C string argument called
"context" or similar, where the input context identifies itself much as it
already does when throwing more specialized diagnostics.
Example:
$ printf '.rn\n' | ./build/test-groff -ww
troff:<standard input>:1: warning: renaming request expects arguments [-w
missing]
$ printf '.rn .T\n' | ./build/test-groff -ww
troff:<standard input>:1: warning: renaming request expects two arguments [-w
missing]
$ printf '.rn .T \002\n' | ./build/test-groff -ww
troff:<standard input>:1: error: ignoring identifier containing invalid
character code 2
See how much less helpful the error diagnostic is than the warnings?
If we look at the source, we can imagine a straightforward improvement.
static void rename_macro() // .rn
{
if (!has_arg()) {
warning(WARN_MISSING, "renaming request expects arguments");
skip_line();
return;
}
symbol s1 = read_identifier();
if (s1.is_null()) {
// The identifier was garbage, like `a\&b`.
skip_line();
return;
}
if (!has_arg()) {
warning(WARN_MISSING, "renaming request expects two arguments");
skip_line();
return;
}
symbol s2 = read_identifier();
if (s2.is_null()) {
// The identifier was garbage, like `a\&b`.
skip_line();
return;
}
request_dictionary.rename(s1, s2);
skip_line();
}
What if we had...
symbol s1 = read_identifier("renaming request");
...
symbol s2 = read_identifier("renaming request");
...instead?
A counter-argument is that this "context" argument might have to be tediously
passed through multiple layers of API. However, (1) it might be worth it; and
(2) there's only one place I know of off the top of my head where one "reader"
function calls another, and that's in the numeric expression parser (because
the set of characters that's valid at the _start_ of a numeric expression is
nonidentical with the set that's valid to _continue_ one).
There's probably another, in the callers of `read_character_in_copy_mode()`.
Still, might be worth it.
_______________________________________________________
Reply to this item at:
<https://savannah.gnu.org/bugs/?68818>
_______________________________________________
Message sent via Savannah
https://savannah.gnu.org/
signature.asc
Description: PGP signature
