I have two Lua functions that manipulate their arguments in different ways, and two new ConTeXt macros defined to let me use them.
I tried to nest them together, but they seem to also be consuming and manipulating the macro calls themselves, not just their outputs. In one nesting order this causes incorrect output, and in the other order it causes a fatal error during typesetting.
This is the MWE:
\startluacode
userdata = userdata or {}
function userdata.lastWords(s)
context(s:sub(s:find(" ") + 1))
end
function userdata.changeCase(s)
context(s:upper())
end
\stopluacode
\define[1]\ChangeCase{\ctxlua{userdata.changeCase([==[#1]==])}}
\define[1]\LastWords{\ctxlua{userdata.lastWords([==[#1]==])}}
\starttext
\LastWords{One Two Three A}\par % => "Two Three A"
\ChangeCase{One Two Three B}\par % => "ONE TWO THREE B"
\LastWords{\ChangeCase{One Two Three C}}\par % => "One Two Three C", wrong output
%\ChangeCase{\LastWords{One Two Three D}}\par % uncomment for fatal error
\stoptext
The problem with C is that \LastWords consumes all of \ChangeCase{One Two Three C} and therefore strips off \ChangeCase. This means the first word is not being removed, and the string isn't up-cased either.
The problem with D is that it changes the case of \LastWords, so it fails with a fatal error because \LASTWORDS is not a macro:
! Undefined control sequence
l.1 \LASTWORDS
{ONE TWO THREE D}
Both of these should be consuming only the output of each other, not consuming each other's macro calls. I don't know how to define them that way though.
(The MWE version of \ChangeCase is boring, I know. My real Lua code is attempting more interesting case changing, but this stub illustrates the core problem: that the Lua code is being passed the macro names too.)
I'm new to using Lua in ConTeXt and I'm not really familiar with the internals of ConTeXt. Maybe there's an obvious thing I'm missing or doing wrong.
My fallback is to give up on separating these macros and define an ad hoc \LastWordsChangeCase macro just for this use. That's a bit gross but will work for this project. I want to be able to make robust Lua macros in the future though, and these ones are unexpectedly fragile. I must be doing something wrong.
The documentation for Lua on the ConTeXt wiki is basic and doesn't seem to cover this. I have no idea what search terms to try on the wiki or the mailing list that might illuminate this problem.
How do I make this work as intended?
\defineexpandableinstead of\define, but the solution will remain very fragile and continue to break if you feed it unexpandable material. – Henri Menke Oct 05 '20 at 15:54\defineexpandabledoes fix the immediate problem and seems stable in my specific document. However, I don’t understand why. I also don’t understand why it might break by feeding it “unexpandable material”, since I don’t know what that is. As I said, I’m not familiar with ConTeXt internals, so when the abstraction breaks, I don’t know why. :) – SevenSidedDie Oct 05 '20 at 17:50\ChangeCase{\framed{foo}}will still break. – Henri Menke Oct 06 '20 at 08:41\defineexpandableworks is because it makes the inner macro expand before being fed to the outer macro—the outer macro "should" be defined that way but doesn't need to be in that specific case. As for things like\framed, that makes sense: the result (expansion?) isn't going to be plain text, which is what my macros expect. I think I understand a bit more now. – SevenSidedDie Oct 07 '20 at 06:39