15

I'm trying to find a way to use the e-TeX \scantokens command inside [the equivalent of] an \edef (well, actually an \xdef, but presumably this does not make any real difference since that's just the \global version of \edef). The reason I am doing this is that it seems to be needed to answer this question about hyperref and pdf outlines.

The problem seems to be that \scantokens doesn't actually expand to the tokens scanned; it actually expands to nothing, but arranges for the next tokens to be read from a pseudo-file named " ", which is then treated almost exactly like a real file, including the check for "runaway" conditions. (See page 10 of the e-TeX manual.)

There is one problem with this explanation, though: why would \input work, as it evidently does in the other question?

Anyway, here's a fairly minimal example:

%&eplain

% make \scantokens pseudo-files show up with name " " in the output
\tracingscantokens=1

\edef\foo{\scantokens{Test}}

Which produces the output

Running `TeX' on `scantokens-in-edef-minimal' with ``pdftex  -interaction=nonstopmode "\input" "scantokens-in-edef-minimal"''
This is pdfTeX, Version 3.1415926-1.40.11 (MiKTeX 2.9)
entering extended mode
(e:\home\tex\scantokens-in-edef-minimal.tex ( )
Runaway definition?
->Test 
! File ended while scanning definition of \foo.
<inserted text> 
                }
l.6 \edef\foo{\scantokens{Test}
                               }
! Too many }'s.
l.6 \edef\foo{\scantokens{Test}}

)
! Emergency stop.
<*> \input scantokens-in-edef-minimal

!  ==> Fatal error occurred, no output PDF file produced!
Transcript written on scantokens-in-edef-minimal.log.

TeX exited abnormally with code 1 at Thu Jan 27 12:24:57

Is it perhaps possible to somehow do the expansion with macros and then do a \def instead of the \edef, so the expansion would be finished before the definition began, and the "runaway definition" condition avoided?

SamB
  • 2,837

1 Answers1

17

In short, yes. Try

\edef\foo{\scantokens{Test\noexpand}}

or

\everyeof={\noexpand}
\edef\foo{\scantokens{text}}

in which the \noexpand ‘hides’ the EOF marker by turning it into \relax.

You might like to take a look at the definition of expl3's \tl_rescan:nn and \tl_set_rescan:Nnn, both wrappers for \scantokens based on the ideas in Heiko Oberdiek's catchfile package.

Then you can write

\tl_set_rescan:Nnn \foo { «catcode setup» } { «rescanned tokens» }

or

\tl_rescan:nn { «catcode setup» } { «tokens to re-scan (in a group)» }

The latter cannot be used inside an \edef but can otherwise come in handy when you're processing a macro argument that has already been tokenised. In practise I find the set_rescan version a little more convenient.

  • 1
    And if you were using luatex, you could use \scantextokens instead of the \everyeof hack. – Taco Hoekwater Jan 28 '11 at 09:06
  • @Taco—Indeed! Sorry I omitted it :) – Will Robertson Jan 28 '11 at 10:42
  • Cool; I'll relay this answer back to the asker of the other question. I was afraid it would be necessary to essentially implement \edef/\xdef from scratch! – SamB Jan 28 '11 at 18:05
  • Now if only I could understand how making the } act like \relax would fool e-TeX into finishing the \scantokens without doing the runaway checks, then I'd ... well, I guess I might just have a headache, actually :-). – SamB Jan 28 '11 at 19:30
  • Pulls up source2e.pdf to find out what LaTeX does to \input that prevents this from happening... *
  • – SamB Jan 28 '11 at 19:32
  • @SamB—it's not the } that is affected by the \noexpand but the invisible end-of-file marker inserted by \scantokens. – Will Robertson Jan 29 '11 at 02:36
  • @Will: Where can I read about this marker? It's obviously not going to be in Volume B, and MiKTeX doesn't seem to have a working "weave" (though I guess I could weave it on my Debian box if worst came to worst)... – SamB Jan 29 '11 at 21:15
  • 2
    @SamB—Perhaps this thread may be instructive: https://groups.google.com/group/comp.text.tex/browse_thread/thread/854391a7654a467d/6825ae391f0a7bab – Will Robertson Jan 30 '11 at 02:17