Skip to content

Instantly share code, notes, and snippets.

@zr-tex8r
Created December 30, 2019 21:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zr-tex8r/eab7cd60fa2eaa5090f6be2e7b24c99c to your computer and use it in GitHub Desktop.
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 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
Copy link
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