7

I have the following code which sets up a XeLaTeX document (to be run with --enable-write18) and defines two commands, \showme and \prRaise:

\documentclass[a4paper,oneside]{memoir}
\usepackage{polyglossia}
\setdefaultlanguage{english}
\usepackage{catchfile}

% ----------------------------------------------------------------------------------------------------------
\newcommand*{\showme}[1]{The value is #1.}

% ----------------------------------------------------------------------------------------------------------
\makeatletter%
\newdimen\pr@Height%

\newcommand{\prRaise}[2]{%
  \setlength{\pr@Height}{\f@size pt}%
  \raisebox{#1\pr@Height}{#2}%
  }%

\makeatother%

% ----------------------------------------------------------------------------------------------------------
\def\cxltxReadA{\CatchFileEdef{\cxltxR}{/tmp/CXLTXtempout.tex}{}}
\def\cxltxReadB{\CatchFileEdef{\cxltxR}{/tmp/CXLTXtempout.tex}{}\cxltxR}

% ----------------------------------------------------------------------------------------------------------
\begin{document}

\showme{foobar}% => ”The value is foobar”.

\showme{\cxltxReadB}% => ”The value is -0.2 .”

\showme{\input{/tmp/CXLTXtempout}}% => ”The value is -0.2 .”

\showme{\immediate\input{/tmp/CXLTXtempout}}% => ”The value is -0.2 .”

A\prRaise{-0.2}{B}C% => "ABC" with B lowered

A\prRaise{-0.2 }{B}C% => "ABC" with B lowered (trailing space shouldn't be a problem)

A\prRaise{\input{/tmp/CXLTXtempout}}{B}C% !!! => Missing number, treated as zero.

A\prRaise{\immediate\input{/tmp/CXLTXtempout}}{B}% !!! => Missing number, treated as zero.

A\prRaise{\cxltxReadB}{B}C% !!! => Missing number, treated as zero.

\cxltxReadA A\prRaise{\cxltxR}{B}C% works

\end{document}

The thing is, trying to use the file contents with \input, \immediate\input and \cxltxReadA ... \cxltxR works for both commands, but, crucially, all attempts to read the file contents within the argument to prRaise fail. It'd be great if it did work, because what i really want to do is call some external program to do a contextual computation, so i want to call that external program at exactly the point where i need it.

Why do the two commands behave so differently? What in the definition of \prRaise causes these troubles?

Ruben
  • 13,448
flow
  • 1,783
  • Just for comparison. When you are using TeX (or XeTeX, but not LaTeX) then it is sufficient to write A\raise\input file.tex em\hbox{B}C instead of your \prRaise. LaTeX brings only complications. – wipet Nov 26 '14 at 19:23

2 Answers2

6

As explained in Why is \input not expandable?, the LaTeX version of \input is not expandable. To use \input in the context you'd like to, as part of setting a length, you need everything to work by expansion. There are good reasons why LaTeX's \input is defined the way it is, but if you really need expansion use the saved TeX primitive:

\begin{filecontents*}{\jobname.tmp}
-0.2
\end{filecontents*}
\documentclass{article}
\makeatletter
\newlength\pr@Height
\newcommand\prRaise[2]{%
  \setlength\pr@Height{\f@size pt}%
  \raisebox{#1\pr@Height}{#2}%
}
\let\normalinput\@@input
\makeatother
\begin{document}
A\prRaise{\normalinput \jobname.tmp }{B}C
\end{document}

Note that the same issue applies to e.g.

A\prRaise{\cxltxReadB}{B}C%

as \cxltxReadB is trying to carry out a definition, which is again not expandable.

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
  • What are those good reasons? I thought the only thing was making it to understand both “syntaxes”: TeX \input file and LaTeX \input{file}. – Manuel Nov 26 '14 at 17:51
  • @Manuel Try using the primitive with an incorrect file name, then try the same with the LaTeX version. In particular, imagine you are doing this 20+ years ago on a session taking several minutes per page where 'bail out' with a incorrect name is not a good option and some form of graceful recover is better! (The 'look ahead' for { breaks expandability in any case.) – Joseph Wright Nov 26 '14 at 17:54
  • 1
    @Manuel In case you miss it :-) What you'll find is that with the primitive you are basically stuck: either you have to be able to sort out the file name or kill the run. With the LaTeX version you can skip the loading entirely and just 'push on': as I say, important in the past. – Joseph Wright Nov 26 '14 at 17:58
  • 1
    @Manuel The LaTeX version also knows about \input@path. – Joseph Wright Nov 26 '14 at 17:59
  • @JosephWright "you have to be able to sort out the file name or kill the run" ... this is not exactly true. You can type "null" and go on. Because there exists an empty file null.tex in TeX distributions. – wipet Nov 26 '14 at 20:20
  • @wipet I'd argue having 'just press enter to skip' is more natural/user friendly, but your point is of course valid. – Joseph Wright Nov 26 '14 at 20:44
  • is that true, we have to put up with this expandable / not expandable conundrum just because LL thought that 'hey, this is 1984, let's not type null any more, just hit enter'!? – flow Nov 27 '14 at 13:36
  • @flow As noted, the LaTeX syntax for \input requires braces (as with essentially all LaTeX mandatory arguments). However, LaTeX also allows the primitive syntax and that is used a lot. The two cases can't be tested for expandably, sot this cannot be changed. Also, as I've noted, the LaTeX version makes use of \input@path which is then used for example by \includegraphics/\graphicspath. That again cannot be fully implemented in an expandable manner. – Joseph Wright Nov 27 '14 at 13:38
  • @flow As documented for LaTeX there is no reason to think \input can be used in anything other than non-expansion contexts. (I note that ConTeXt also makes \input non-expandable: it's not just LaTeX!) I'd ask how often in a structural sense it's necessary to use \input in an expansion context: I'd expect configuration or similar files to be written either to be executed (like the .aux file) or parsed (using \read/\readline) in almost every case. – Joseph Wright Nov 27 '14 at 13:42
  • this thinking permeates this culture. reading from a file is one of the few ways for TeX to communicate to outside processes, but that gets easily dismissed. quoting a precedent: "Neither Donald Knuth nor Leslie Lamport seems to have given much thought to the case where the paragraph skip has a positive natural width. Leslie Lamport dismisses all potential difficulties with the remark that 'it is customary not to leave any extra space between paragraphs'."—Victor Eijkhout, TUGboat 4/1990. makes me crazy when i ask how to put vertical rules into a table and people scream "you don't do that". – flow Nov 27 '14 at 14:15
  • @flow I didn't say reading from secondary files was out, though at (La)TeX is a typesetting system there is some argument in terms of having the content well-defined and self-contained. You can happily read from a file in a variety of ways, most obviously by arranging them to read in the form \def\foo{setting} as is done for example by the .aux mechanism, also by using as I say \read on a line-by-line basis or indeed using catchfile (more recent). As I say, the position is this behaviour cannot be changes in LaTeX2e (stability): design decisions for LaTeX3 are one for chat! – Joseph Wright Nov 27 '14 at 14:25
3

First of all let's get rid of \immediate, which makes sense only before \write, \openout and \closeout, because these operations are normally performed during shipout and with the \immediate prefix they are performed “immediately”.

So \immediate\input has just one effect, that of expanding \input until the first unexpandable token is found. Since this token is \let (resulting from \@ifnextchar), TeX ignores \immediate and moves on.

I would distinguish your calls of \prRaise between an “explicit” argument and a file to be read in.

\begin{filecontents*}{\jobname.tmp}
-0.2
\end{filecontents*}
\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand\prRaise{smm}
 {
  \dim_set:Nn \l_flow_pr_height_dim { \use:c { f@size } pt }
  \IfBooleanTF{#1}
   {% \tl_set_from_file:Nnn is the same as \CatchFileDef
    \tl_set_from_file:Nnn \l_flow_pr_factor_tl { } { #2 }
   }
   {
    \tl_set:Nn \l_flow_pr_factor_tl { #2 }
   }
  \flow_pr_raise:n { #3 }
 }

\dim_new:N \l_flow_pr_height_dim
\tl_new:N \l_flow_pr_factor_tl

\cs_new_protected:Npn \flow_pr_raise:n #1
 {
  \raisebox{ \l_flow_pr_factor_tl \l_flow_pr_height_dim } { #1 }
 }

\ExplSyntaxOff

\begin{document}
A\prRaise{-0.2}{B}C

A\prRaise*{\jobname.tmp}{B}C
\end{document}

enter image description here

egreg
  • 1,121,712
  • thanks for your effort... one more piece to demonstrate that doing TeX is more of an art than a science. this looks like black magic to me. – flow Nov 27 '14 at 12:26