Created
December 30, 2019 21:58
-
-
Save zr-tex8r/eab7cd60fa2eaa5090f6be2e7b24c99c to your computer and use it in GitHub Desktop.
LaTeX: to define a macro that needs only one expansion to get the result
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% | |
%% This is file 'ixquickmacro.sty'. | |
%% | |
%% Copyright (c) 2019 Takayuki YATO (aka. "ZR") | |
%% GitHub: https://github.com/zr-tex8r | |
%% Twitter: @zr_tex8r | |
%% | |
%% This package is distributed under the MIT License. | |
%% | |
%% package declaration | |
\NeedsTeXFormat{LaTeX2e} | |
\ProvidesPackage{ixquickmacro}[2019/12/31 v0.2] | |
\def\ixzqm@pkgname{ixquickmacro} | |
%--------------------------------------- environment check | |
% engine | |
\RequirePackage{iftex} | |
\RequireLuaTeX | |
\unless\ifprimitive\luadef | |
\PackageError\ixzqm@pkgname | |
{Your LuaTeX does not support \string\luadef}% | |
{Package loading is aborted.}% | |
\expandafter\endinput\fi\relax | |
%--------------------------------------- general | |
%% packages | |
\RequirePackage{etoolbox}[] | |
%% unique tokens | |
\def\ixzqm@uniq{\@gobble\ixzqm@uniq@}% vanishing | |
%% constants | |
\chardef\ixzqm@iconst=42 | |
%% variables | |
\newif\ifixzqm@ok | |
\newluafunction\ixzqm@lua@setglobal | |
\newluafunction\ixzqm@lua@prepare | |
\newluafunction\ixzqm@lua@declare | |
\let\ixzqm@probe\relax | |
\let\ixzqm@body\relax | |
\let\ixzqm@lua@target\relax | |
%--------------------------------------- Lua part | |
\begingroup | |
\escapechar\m@ne | |
\catcode10=12 \endlinechar=10 % | |
\directlua{ | |
local M = {}; xx = M | |
local functable = lua.get_functions_table() | |
local rnumber = luatexbase.registernumber | |
local create, put_next = token.create, token.put_next | |
local insert, unpack = table.insert, table.unpack | |
functable[rnumber('ixzqm@lua@setglobal')] = function() | |
M.global = (token.scan_int() > 0) | |
end | |
functable[rnumber('ixzqm@lua@prepare')] = function() | |
-- save prefix info | |
-- TODO: what to do with 'global'? | |
local probe = token.create("ixzqm@probe") | |
M.protected = probe.protected | |
-- check if the target token (CS) is definable | |
M.targetcs = token.get_next() | |
put_next(create('let'), M.targetcs, create('ixzqm@uniq'), | |
create('ifx'), M.targetcs, create('ixzqm@uniq'), | |
create('ixzqm@next'), M.targetcs, create('fi'), | |
create('ixzqm@fail')) | |
end | |
functable[rnumber("ixzqm@lua@declare")] = function() | |
local id = rnumber('ixzqm@lua@target') | |
local drive = create('ixzqm@drive') | |
local delegate = create('ixzqm@D/'..tostring(id)) | |
functable[id] = function() | |
put_next(drive, delegate) | |
token.scan_int() -- trigger full expansion | |
end | |
-- write out luadef statement | |
local t = {create('luadef'), M.targetcs, create('ixzqm@lua@target')} | |
if M.global then insert(t, 1, create('global')) end | |
if M.protected then insert(t, 1, create('protected')) end | |
put_next(unpack(t)) | |
end | |
}\endgroup% | |
%--------------------------------------- TeX part | |
%%<*> [\protected]\defquickmacro\CS{<body>} | |
\newrobustcmd*{\defquickmacro}{% | |
\luafunction\ixzqm@lua@setglobal\z@ | |
\ixzqm@def@quick@macro@a | |
} | |
\newrobustcmd*{\gdefquickmacro}{% | |
\luafunction\ixzqm@lua@setglobal\@ne | |
\ixzqm@def@quick@macro@a | |
} | |
\def\ixzqm@def@quick@macro@a#1#{% | |
% once pass through TeX processor | |
\ixzqm@def@quick@macro@b#1% | |
} | |
\def\ixzqm@def@quick@macro@b{% | |
\def\ixzqm@probe{}% dummy def for picking up macro prefixes | |
\let\ixzqm@next\ixzqm@def@quick@macro@c | |
\luafunction\ixzqm@lua@prepare | |
} | |
% invoked as: \ixzqm@def@quick@macro@c \CS \fi \ixzqm@fail | |
\@gobbletwo\if\if \def\ixzqm@def@quick@macro@c#1\fi#2{\fi | |
% pick up the macro body | |
\afterassignment\ixzqm@def@quick@macro@d | |
\def\ixzqm@body | |
} | |
\def\ixzqm@def@quick@macro@d{% | |
\newluafunction\ixzqm@lua@target | |
\global\cslet{ixzqm@D/\number\ixzqm@lua@target}\ixzqm@body | |
\luafunction\ixzqm@lua@declare | |
} | |
%% \ixzqm@fail | |
% Gobbles the macro body. | |
\def\ixzqm@fail{% | |
\afterassignment\ixzqm@fail@a | |
\gdef\ixzqm@probe | |
} | |
\def\ixzqm@fail@a{% | |
\global\let\ixzqm@probe\@empty | |
} | |
%% \ixzqm@drive\CS | |
% A execution of scan_int triggers the full expansion. | |
\def\ixzqm@drive#1{% | |
\expandafter\ixzqm@iconst\expanded{#1}% | |
} | |
%--------------------------------------- done | |
\endinput | |
%% EOF |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
\documentclass{article} | |
\usepackage{ixquickmacro} | |
\makeatletter %!!!!!!!!!!!!!!!!!!!!!!!!! TeX code BEGIN | |
% to show the expanded forms | |
\def\xInspect#1{\typeout{% | |
once->\unexpanded\expandafter{#1}^^J% | |
twice->\unexpanded\expandafter\expandafter\expandafter{#1}^^J% | |
full->#1}} | |
%% \todayYMD *once-expands* to the desired token list. | |
\defquickmacro\todayYMD{% | |
% this code is "only fully-ecpandable" | |
\the\year/\two@digits\month/\two@digits\day} | |
%% \todayYMDp is truly unexpandable. | |
\protected\defquickmacro\todayYMDp{% | |
\the\year/\two@digits\month/\two@digits\day} | |
\makeatother %!!!!!!!!!!!!!!!!!!!!!!!!! TeX code END | |
\begin{document} | |
\xInspect{\todayYMD} | |
% once->2019/12/31 | |
% twice->2019/12/31 | |
% full->2019/12/31 | |
\stop |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage
\defquickmacro\CS{<body>}
: Defines locally\CS
to be a token that once-expands to the “full expansion of<body>
”.\gdefquickmacro\CS{<body>}
: Ditto except that the definition is global.