Checking for a macro in a string without expanding it
As the subject of this question suggests, this is really more of a question about expansion control (a topic that is still a bit obscure to me). Suppose I have a macro \inner that expects a single argument or an assignment of parameters in brackets. For my purposes, I don't want this macro to do anything when it is typeset, so I'll just define it as empty: ``` \def\inner[#1]\empty ``` Now suppose I have another macro \outer that invokes this macro with some specific input and sets some plain text after it: ``` \def\outer{\inner[123] etc.} ``` What I'd like to do is parse the argument of \inner in \outer. I was hoping that a string search in Lua would work, but I'm not having any luck. A minimal (non)-working example is included below: ``` \def\inner[#1]\empty \def\outer{\inner[123] etc.} \startluacode local userdata = userdata or {} function userdata.parseinner(str) local innerparams = "" if string.find(str, "\\inner(%b[])") then i, j = string.find(str, "\\inner(%b[])") innerparams = string.sub(str, i+1, j-1) -- we just want the content inside the brackets end context(innerparams) return end \stopluacode \def\parseinner#1{\ctxlua{userdata.parseinner([==[#1]==])}} \starttext Testing:\blank \parseinner{\outer} \stoptext ``` My problem is that when I pass \outer to the \parseinner macro, it gets fully expanded, so there isn't anything left to match "\\inner%b[]". Is there a way to expand \outer when I pass it to the \parseinner macro without also expanding the \inner macro inside it? Or is there some other preferred way of doing this? Joey
If I understand it correctly, you may need something like this...? % Protection is key \protected\def\inner[#1]{\empty} % \outer is (or was) already defined in \CONTEXT % Please use another name \def\Outer{\inner[123] and \inner[some text] etc.} \startluacode local implement = interfaces.implement local argument = tokens.scanners.argument local function parseinner() local r = {} local str = argument() str = str:gsub("\\inner%s*(%b[])",function(s)r[#r+1] = s:sub(2,#s-1)end) context(table.concat(r," ")) -- Change " " by another spacer if needed end implement{name = "parseinner", public = true, actions = parseinner} \stopluacode \starttext \parseinner{\Outer} \stoptext However, this will only work with very simple cases (no nesting, etc.). Hope this helps. Best regards, Jairo El mar, 16 de nov. de 2021 a la(s) 14:22, Joey McCollum via ntg-context ( ntg-context@ntg.nl) escribió:
As the subject of this question suggests, this is really more of a question about expansion control (a topic that is still a bit obscure to me). Suppose I have a macro \inner that expects a single argument or an assignment of parameters in brackets. For my purposes, I don't want this macro to do anything when it is typeset, so I'll just define it as empty:
``` \def\inner[#1]\empty ```
Now suppose I have another macro \outer that invokes this macro with some specific input and sets some plain text after it:
``` \def\outer{\inner[123] etc.} ```
What I'd like to do is parse the argument of \inner in \outer. I was hoping that a string search in Lua would work, but I'm not having any luck. A minimal (non)-working example is included below:
```
\def\inner[#1]\empty
\def\outer{\inner[123] etc.}
\startluacode
local userdata = userdata or {}
function userdata.parseinner(str)
local innerparams = ""
if string.find(str, "\\inner(%b[])") then
i, j = string.find(str, "\\inner(%b[])")
innerparams = string.sub(str, i+1, j-1) -- we just want the content inside the brackets
end
context(innerparams)
return
end
\stopluacode
\def\parseinner#1{\ctxlua{userdata.parseinner([==[#1]==])}}
\starttext
Testing:\blank
\parseinner{\outer}
\stoptext ```
My problem is that when I pass \outer to the \parseinner macro, it gets fully expanded, so there isn't anything left to match "\\inner%b[]". Is there a way to expand \outer when I pass it to the \parseinner macro without also expanding the \inner macro inside it? Or is there some other preferred way of doing this?
Joey
___________________________________________________________________________________ If your question is of interest to others as well, please add an entry to the Wiki!
maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context webpage : http://www.pragma-ade.nl / http://context.aanhet.net archive : https://bitbucket.org/phg/context-mirror/commits/ wiki : http://contextgarden.net
___________________________________________________________________________________
On 11/16/2021 9:09 PM, Jairo A. del Rio via ntg-context wrote:
If I understand it correctly, you may need something like this...?
% Protection is key \protected\def\inner[#1]{\empty} % \outer is (or was) already defined in \CONTEXT % Please use another name \def\Outer{\inner[123] and \inner[some text] etc.} \startluacode local implement = interfaces.implement local argument = tokens.scanners.argument local function parseinner() local r = {} local str = argument() str = str:gsub("\\inner%s*(%b[])",function(s)r[#r+1] = s:sub(2,#s-1)end) context(table.concat(r," ")) -- Change " " by another spacer if needed end implement{name = "parseinner", public = true, actions = parseinner} \stopluacode \starttext \parseinner{\Outer} \stoptext
However, this will only work with very simple cases (no nesting, etc.). Hope this helps. well, if we start talking weird code ...
\starttext \tolerant\def\MyInnerOuter#1\MyInner[#2]#3\MyDone\ignorearguments{#2} \def\MyOuter#1{\MyInnerOuter#1\MyDone\ignorearguments} whatever: \MyOuter{\MyInner[oeps]}\par whatever: \MyOuter{\InnerMy[oeps]}\par \edef\ItWorksA{\MyOuter{\MyInner[oeps]}} \edef\ItWorksB{\MyOuter{\InnerMy[oeps]}} whatever: \meaningless\ItWorksA\par whatever: \meaningless\ItWorksB\par \stoptext (1) we're tolerant so no problem when no match (2) the ignore hack quits scanning because tex will keep looking it's pretty fast and needs no lua magic, only a twisted mind Hans ----------------------------------------------------------------- Hans Hagen | PRAGMA ADE Ridderstraat 27 | 8061 GH Hasselt | The Netherlands tel: 038 477 53 69 | www.pragma-ade.nl | www.pragma-pod.nl -----------------------------------------------------------------
I'm not sure if my mind is twisted enough! I can follow how Jairo's answer works, and that does what I need it to, but it's not as clear to me what the last two TeX-only approaches are doing at a low level. Perhaps more importantly, I should clarify that for my purposes, the \MyOuter macro is the \currentbtxrighttext macro, which is defined in a separate module that I'd prefer not to modify. I want to be able to detect and parse the parameters of a \loc macro that a user can specify in a citation as follows: ``` \cite[lefttext={See}, righttext={\loc[vol=8,p=223] for further details}][clementinehomilies] ``` Because the locator parameters may need to be parenthesized or formatted differently depending on the category of the bibliography entry, they should be typeset separately from the plain part of the righttext. This is why I'd like to be able to parse the parameters and then expand the \loc macro itself as empty when the righttext is typeset. Joey On Tue, Nov 16, 2021 at 4:19 PM Hans Hagen via ntg-context < ntg-context@ntg.nl> wrote:
On 11/16/2021 9:09 PM, Jairo A. del Rio via ntg-context wrote:
If I understand it correctly, you may need something like this...?
% Protection is key \protected\def\inner[#1]{\empty} % \outer is (or was) already defined in \CONTEXT % Please use another name \def\Outer{\inner[123] and \inner[some text] etc.} \startluacode local implement = interfaces.implement local argument = tokens.scanners.argument local function parseinner() local r = {} local str = argument() str = str:gsub("\\inner%s*(%b[])",function(s)r[#r+1] = s:sub(2,#s-1)end) context(table.concat(r," ")) -- Change " " by another spacer if needed end implement{name = "parseinner", public = true, actions = parseinner} \stopluacode \starttext \parseinner{\Outer} \stoptext
However, this will only work with very simple cases (no nesting, etc.). Hope this helps. well, if we start talking weird code ...
\starttext
\tolerant\def\MyInnerOuter#1\MyInner[#2]#3\MyDone\ignorearguments{#2}
\def\MyOuter#1{\MyInnerOuter#1\MyDone\ignorearguments}
whatever: \MyOuter{\MyInner[oeps]}\par whatever: \MyOuter{\InnerMy[oeps]}\par
\edef\ItWorksA{\MyOuter{\MyInner[oeps]}} \edef\ItWorksB{\MyOuter{\InnerMy[oeps]}}
whatever: \meaningless\ItWorksA\par whatever: \meaningless\ItWorksB\par
\stoptext
(1) we're tolerant so no problem when no match (2) the ignore hack quits scanning because tex will keep looking
it's pretty fast and needs no lua magic, only a twisted mind
Hans
----------------------------------------------------------------- Hans Hagen | PRAGMA ADE Ridderstraat 27 | 8061 GH Hasselt | The Netherlands tel: 038 477 53 69 | www.pragma-ade.nl | www.pragma-pod.nl -----------------------------------------------------------------
___________________________________________________________________________________ If your question is of interest to others as well, please add an entry to the Wiki!
maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context webpage : http://www.pragma-ade.nl / http://context.aanhet.net archive : https://bitbucket.org/phg/context-mirror/commits/ wiki : http://contextgarden.net
___________________________________________________________________________________
On 11/16/2021 11:15 PM, Joey McCollum via ntg-context wrote:
I'm not sure if my mind is twisted enough! I can follow how Jairo's answer works, and that does what I need it to, but it's not as clear to me what the last two TeX-only approaches are doing at a low level.
Perhaps more importantly, I should clarify that for my purposes, the \MyOuter macro is the \currentbtxrighttext macro, which is defined in a separate module that I'd prefer not to modify. I want to be able to detect and parse the parameters of a \loc macro that a user can specify in a citation as follows:
``` \cite[lefttext={See}, righttext={\loc[vol=8,p=223] for further details}][clementinehomilies] ```
Because the locator parameters may need to be parenthesized or formatted differently depending on the category of the bibliography entry, they should be typeset separately from the plain part of the righttext. This is why I'd like to be able to parse the parameters and then expand the \loc macro itself as empty when the righttext is typeset. in that case: just redefine \loc on the fly depending on where it's used and/or use keys
\cite[lefttext={See},volume=8,page=223] or so .. imo parsing content is not really a good solution and probably also not reliable Hans ----------------------------------------------------------------- Hans Hagen | PRAGMA ADE Ridderstraat 27 | 8061 GH Hasselt | The Netherlands tel: 038 477 53 69 | www.pragma-ade.nl | www.pragma-pod.nl -----------------------------------------------------------------
On 11/17/2021 12:36 PM, Henning Hraban Ramm via ntg-context wrote:
Am 16.11.2021 um 22:18 schrieb Hans Hagen via ntg-context
: it's pretty fast and needs no lua magic, only a twisted mind
May I quote you as “ConTeXt needs a twisted mind”? ;D
How about "Context tries to untwist your tex mind". After all this is not really user code is it? ----------------------------------------------------------------- Hans Hagen | PRAGMA ADE Ridderstraat 27 | 8061 GH Hasselt | The Netherlands tel: 038 477 53 69 | www.pragma-ade.nl | www.pragma-pod.nl -----------------------------------------------------------------
On 11/16/2021 8:22 PM, Joey McCollum via ntg-context wrote:
As the subject of this question suggests, this is really more of a question about expansion control (a topic that is still a bit obscure to me). Suppose I have a macro \inner that expects a single argument or an assignment of parameters in brackets. For my purposes, I don't want this macro to do anything when it is typeset, so I'll just define it as empty:
``` \def\inner[#1]\empty ```
Now suppose I have another macro \outer that invokes this macro with some specific input and sets some plain text after it:
``` \def\outer{\inner[123] etc.} ```
What I'd like to do is parse the argument of \inner in \outer. I was hoping that a string search in Lua would work, but I'm not having any luck. A minimal (non)-working example is included below:
```
\def\inner[#1]\empty
\def\outer{\inner[123] etc.}
\startluacode
local userdata = userdata or {}
function userdata.parseinner(str)
local innerparams = ""
if string.find(str, "\\inner(%b[])") then
i, j = string.find(str, "\\inner(%b[])")
innerparams = string.sub(str, i+1, j-1) -- we just want the content inside the brackets
end
context(innerparams)
return
end
\stopluacode
\def\parseinner#1{\ctxlua{userdata.parseinner([==[#1]==])}}
\starttext
Testing:\blank
\parseinner{\outer}
\stoptext
```
My problem is that when I pass \outer to the \parseinner macro, it gets fully expanded, so there isn't anything left to match "\\inner%b[]". Is there a way to expand \outer when I pass it to the \parseinner macro without also expanding the \inner macro inside it? Or is there some other preferred way of doing this?
One can always abuse native tex: \starttext \def\MyOuter#1% {\beginlocalcontrol % hides the next \let\Indeed\empty \def\MyInner[##1]{\gdef\Indeed{##1}}% \setbox\scratchbox\hpack{#1}% \endlocalcontrol % but only if needed \Indeed} whatever: \MyOuter{\MyInner[oeps]}% \edef\ItWorks{\MyOuter{\MyInner[oeps]}} whatever: \meaningless\ItWorks \stoptext -- ----------------------------------------------------------------- Hans Hagen | PRAGMA ADE Ridderstraat 27 | 8061 GH Hasselt | The Netherlands tel: 038 477 53 69 | www.pragma-ade.nl | www.pragma-pod.nl -----------------------------------------------------------------
participants (4)
-
Hans Hagen
-
Henning Hraban Ramm
-
Jairo A. del Rio
-
Joey McCollum