groff
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Groff] condition: OR of two string comparisons


From: Tadziu Hoffmann
Subject: Re: [Groff] condition: OR of two string comparisons
Date: Wed, 26 Nov 2014 17:31:44 +0100
User-agent: Mutt/1.5.21 (2010-09-15)


> Tadziu's opinion matters to me;  I read every bit of troff
> he posts here because I normally learn something.  :-)

Wow.  I feel flattered.

But lest I come across as a dinosaur born yesterday and stubbornly
resisting progress, let me explain my position a bit better.

[Warning: aimless rambling ahead.]

My opinion is that we're trying to fix a problem that
doesn't actually need fixing.  The syntax of expressions and
conditionals isn't what's keeping new users from using groff.
As Peter has already pointed out, the entire working model
of roff presents a much greater mental hurdle than the one
given by an idiosyncratic expression syntax.  Will someone
already tripping over the syntax of conditionals have the
perseverance to fathom the rest of roff?

(Not to mention the whole structural-vs.-presentational-markup
propaganda that casts roff as unmodern and backward, instead
of simply the low-level engine for higher-level functions
implemented on top of it in terms of macros.)


> The input language is the main criticism of roff today
> (by users who use LaTeX or GUI tools).

Srsly?  No, the input language of roff is not being criticized
by these people for the simple fact that most have never even
heard of roff, let alone seen its input.  Go ask around.

(And, by the way, perl has no shortage of people using it
despite being a write-only language.)


> Where would LaTeX be without the many users that do step
> beyond it into TeX to write extra packages that do all kinds
> of different things?

Exactly.  And TeX is on par with roff for idiosyncrasy of
language features.  [Is "\advance \x by \y" more "modern"
than ".nr x +\ny"?]  That does not deter those users.
And my guess is neither would roff's expression syntax.


And to see things from a little self-centered perspective:
I use groff because it's a programmable text formatter that
despite its peculiarities is based on a relatively simple
conceptual model that I can understand and that I can usually
coax into doing what I want.  But otherwise I have no stake
in it.  My job doesn't depend on it and I'm not making money
from it.  The number of other users has no bearing on the
usefulness of groff for me, and the enhancements we're
discussing are nothing that I would profit from.

(I do have a few ideas regarding some typesetting aspects
that I would like to experiment with.  But I probably wouldn't
choose groff as my experimental platform because I feel it's
already too cumbersome (and it's written in C++).  Instead,
I'd put my money where my mouth is and start over from
scratch.  Sure, I'd have to reinvent 90% of what's necessary
to accomplish the basic functions.  But I'd also be getting
rid of 90% of the cruft that has accumulated over the years.)


> I'd like to read `.nr i 3+4*6' and know if that's 27 or 42
> without having to know the value of a `new expression syntax'
> register; [...]

That statement is what I had in mind when I said I believe we
shouldn't change the interpretation of numeric expressions.
Of course you could argue that

  .nr i x 3+4*6

(or some other syntax) is visually distinct from

  .nr i 3+4*6

and so the first can rightfully be expected to give 27
whereas the second should yield 42, but instead I think
it's a dreadful opportunity for causing great confusion.

Let me say again that I'm not per se against a "modern" syntax.
What I am against is this terrible clash of two incompatible
traditions in the same package.  I think the python people
did the right thing when they realized some decisions of the
past were suboptimal in the light of recent developments --
make a clean cut and start over without the ballast.
Python 2 and python 3 can coexist as separate programs.
Why shouldn't groff and groff2?

> However, I wonder if a preprocessor could give this new syntax
> à la Ratfor.

Ah.  One of the "cuisinarts of programming" that are
"great for making quiche".  :-D

> Could something work for troff in a similar way, introducing
> its own variables to track state during expression evaluation?
> I don't see why not.

I'm always amazed at the lengths to which people are willing to go...
Although I might consider this an interesting programming exercise,
I have to ask: isn't implementing yet another whole new language
on top of groff much more complicated than implementing groff's
typesetting functions in an existing language like python?  That
would immediately give access to all of python's data types,
functions, and control structures.


                            -=(*)=-


Nevertheless, having said all that, and after asking again
the provocative question whether this is really necessary and
whether it offers something new which couldn't be done with
the existing facilities (laziness doesn't count, unless the
only existing mechanism is inordinately cumbersome), if it's
still decided to implement a new syntax, I'd like to make
some suggestions.

For the treatment of expressions, the main points have already
been made:

 1. Map booleans to numbers.  In roff, positive = true, zero or
    negative = false.  (This is already how it's handled.)

 2. Make string comparison operators and the built-in condition
    tests return appropriate numbers.
    (Logical operators already accept and return appropriate
    numbers.)

That will allow having only one class of "expression" which
can be used by .if and .nr alike (allowing conditional tests
to be assigned to number registers without an additional .if).
My vote for the syntax of these "extended expressions" is
to simply enclose them in brackets:

  .nr i [3+4*6]
  .if ['\\$1'abc':\\n[.$]=0] stuff

I believe this feels much more natural than the other
suggestions offered so far.


Regarding the if/then/else syntax: although I admit that Ralph's
example is easier to read than the original mgm fragment, that's
partly because the original wasn't very good.  (Eight-space
indents?! C'mon! "Everyone knows" that the optimum is two.)
Here's how I might have written it:  (but see below)

  .\" Copy to
  .de NS
  .sp
  .ie !''\\$2' \{.\" 2nd arg flag set: use 1st arg as-is
  .        ds let*str \\$1\}
  .el \{.\" empty/no second arg
  .  ie \\n[.$]>0 \{.\" argument present
  .    ie !\w'\\$1' \{.\" has zero/negative width: use default
  .        ds let*str \\*[Letns!\\*[Letnsdef]]\}
  .    el \{.\" non-empty arg
  .      ie d Letns!\\$1 \{.\" is reference to predefined item
  .        ds let*str \\*[Letns!\\$1]\}
  .      el \{.\" not in list: use as special attribute
  .        ds let*str \\*[Letns!copy](\\$1)\\*[Letns!to]\}\}\}
  .  el \{.\" no args: use default
  .        ds let*str \\*[Letns!\\*[Letnsdef]]\}\}
  .ne 2
  .nf
  \\*[let*str]
  ..

Still, Ralph's ".} else {" construct gets my unrestricted opposition.
Could there be anything more at odds with the general roff syntax?

Yes, braces are familiar to C programmers.  And yes, the
original roff multiline block syntax used braces.  But that's
because it lived outside the normal request/macro mechanism,
and the easiest way to achieve that was using the existing
escape mechanism with one-character delimiters.  (And I can
imagine it already felt like an afterthought at the time.)

But the new syntax is supposed to mesh with roff's request syntax[*],
and the Bourne-shell/Fortran-inspired model is much more agreeable
with that: (I also find it much easier to read.)

  .\" Copy to
  .de NS
  .sp
  .if ['\\$2' != ''] .then
  .    ds let*str \\$1
  .elseif [\\n[.$]] .then
  .  if [width('\\$1') == 0] .then
  .    ds let*str \\*[Letns!\\*[Letnsdef]]
  .  elseif [defined(Letns!\\$1)] .then
  .    ds let*str \\*[Letns!\\$1]
  .  else
  .    ds let*str \\*[Letns!copy](\\$1)\\*[Letns!to]
  .  endif
  .else
  .  ds let*str \\*[Letns!\\*[Letnsdef]]
  .endif
  .ne 2
  .nf
  \\*[let*str]
  ..

[*] Note, however, that I'm not convinced that a request-level
block mechanism would work in all circumstances, due to roff's
line-by-line processing.  I'm convinced that the original
roff inventors did deliberate on how best to implement
multiline conditionals, and chose their syntax out of necessity.
I got bitten once by block-closing braces sitting on a line
by themselves instead of at the end of the last line of the
block, and something similar can happen here, too.



As an aside at the end, I'm surprised that no-one has yet
pointed out the obvious:  we're working with a macro
processor, where the natural block structuring element
is not a tacked-on brace structure, but the *macro*!
Multiline conditionals are completely unnecessary:

  .\" Copy to
  .de NS
  .sp
  .ie \\n[.$]=0 .ds let*str \\*[Letns!\\*[Letnsdef]]
  .el .NS1 \\$@
  .ne 2
  .nf
  \\*[let*str]
  ..
  .de NS1
  .ie !''\\$2' .ds let*str \\$1
  .el .NS2 \\$@
  ..
  .de NS2
  .ie !\w'\\$1' .ds let*str \\*[Letns!\\*[Letnsdef]]
  .el .NS3 \\$@
  ..
  .de NS3
  .ie d Letns!\\$1 .ds let*str \\*[Letns!\\$1]
  .el .ds let*str \\*[Letns!copy](\\$1)\\*[Letns!to]
  ..

which is much more pleasing in terms of roff aesthetics.

And another example, since something similar had come up:

  .de inlist?
  .ie \\n(.$<2 .nr result 0
  .el .inlist1 \\$@
  ..
  .de inlist1
  .ie '\\$1'\\$2' .nr result 1
  .el .inlist2 \\$@
  ..
  .de inlist2
  .ds _1 \\$1
  .shift 2
  .inlist? \\*(_1 \\$@
  ..
  .ds fruit apple banana lemon grape
  .ds myfruit lemon
  .inlist? \*[myfruit] \*[fruit]
  .ie \n[result] Yes, ``\*[myfruit]'' is in ``\*[fruit]''.
  .el             No, ``\*[myfruit]'' is not in ``\*[fruit]''.

Programming roff is a bit like programming in assembler:
complex expressions are built up piece by piece using simple
instructions, and temporary registers are a useful aid, not
something that must be avoided at all costs.





reply via email to

[Prev in Thread] Current Thread [Next in Thread]