Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
LaTeX: to define a macro that needs only one expansion to get the result
%%
%% 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
\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
@zr-tex8r

This comment has been minimized.

Copy link
Owner Author

zr-tex8r commented Dec 30, 2019

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.